题解:P17166 [CEOI 2026] Flower Cutting
lailai0916 · · 题解
题意简述
给定一张无向图。若两个不相邻结点有至少两个公共邻点,就可以连接这两个结点。
输入的图已经无法继续加边。现在删去尽量多的边,要求反复执行上述操作后仍能恢复原图。求最多可以删去多少条边。
解题思路
先确定原图的结构。
取原图中的一条边
任何包含
由此得到两个重要结论:
- 每条边恰好属于一个极大团;
- 两个不同的极大团不会共用边,至多共用一个结点。
一次加边及其四条见证边,在原图中位于同一个四结点团,也就位于同一个极大团。不同极大团之间不会相互帮助。因此,可以分别求出每个极大团内部最多删除的边数,再将答案相加。
设大小为
先给出达到该边数的构造。
当
保留的边数为:
当
下面证明不可能保留更少的边。
把每条初始保留边看成一个只含两个结点的团簇。团簇记录一组初始边,并保证这些边能够生长成该团簇结点集上的完全图。初始团簇本身就是二结点完全图,因此满足这一性质。
在生长过程中,一次操作会遇到一个四元环。若环上的边来自多个团簇,就合并所有涉及的团簇。环的两条对角线可以先被补出,使四个环上结点成为团。每个旧团簇都含有一条环边,所以旧团簇中的其他结点可以借助这条边的两个端点连接到整个四结点团。之后,不同旧团簇中的结点也有两个公共邻点,故合并后的结点集能够生长成团。
若还有团簇与新团簇共用至少两个结点,也可以用这两个公共结点补齐两团之间的边,并将它吸收。反复吸收后,不同团簇至多共用一个结点。
对团簇
初始团簇有两个结点和一条边,恰好取等。
考虑一次四元环合并。设环上共出现
又因为
再考虑吸收一个团簇
所以每次合并和吸收都保持不等式。若初始图能够生长为
最后考虑如何枚举极大团。对于一条尚未处理的边
大小为
设机器字长为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ull=unsigned long long;
const int N=1005;
const int M=100005;
const int W=16;
int eu[M],ev[M],id[N][N],c[N];
ull g[N][W];
bool vis[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>eu[i]>>ev[i];
id[eu[i]][ev[i]]=id[ev[i]][eu[i]]=i;
g[eu[i]][ev[i]>>6]|=1ull<<(ev[i]&63);
g[ev[i]][eu[i]>>6]|=1ull<<(eu[i]&63);
}
int ans=0;
for(int i=1;i<=m;i++)
{
if(vis[i])continue;
int cnt=0;
c[++cnt]=eu[i];
c[++cnt]=ev[i];
for(int j=0;j<W;j++)
{
ull x=g[eu[i]][j]&g[ev[i]][j];
while(x)
{
c[++cnt]=j*64+__builtin_ctzll(x);
x&=x-1;
}
}
for(int j=1;j<=cnt;j++)
{
for(int k=j+1;k<=cnt;k++)vis[id[c[j]][c[k]]]=1;
}
int keep=(3*cnt+1)/2-2;
ans+=cnt*(cnt-1)/2-keep;
}
cout<<ans<<'\n';
return 0;
}