【牛客】沙漠点列(点双联通分量)
前言
Happy Halloween!
题目描述
我们称一张无向图是仙人掌,当且仅当这张无向图连通且每条边最多属于一个简单环。我们称一张无向图是沙漠,当且仅当这张无向图中所有连通子图都是仙人掌。
给出一个 n 个点,m条边的沙漠,你可以删去其中的 k 条边。求能分成的连通块数量最大值。
输入描述:
第一行输入三个自然数 n,m,k(3≤n≤106;0≤k≤m≤2×106)。
接下来 m 行,每行输入两个正整数 u,v(1≤u,v≤n)保证无重边、无自环。
输出描述:
输出一行一个正整数,表示答案。
示例1
输入
5 5 3
1 2
2 3
2 4
2 5
4 5
输出
3
说明
一种最优的方案是:删去 2−4,2−5,4−5这三条边,剩下三个连通块:{1,2,3},{4},{5}。
分析
看错题写了边双,还好出题人给了边双80分
显然贪心,先割掉割边,那么剩下的就是一堆环了
根据仙人掌的性质,我们可以猜测知道环与环最多交于一个点,
这种情况找环应该用点双联通分量
当有两个环交于一点时,完全可以把另一个环缩成交的那个点
所以只需要对每个环单独考虑贡献就好了
每个环必须先割掉一条边,剩下的就都是割边了
人生第一次写读优
Code
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1000005;
const int maxm=2000005;
int n,m,k,ecnt;
int info[maxn],nx[maxm<<1],v[maxm<<1];
int en,id,ans,cnt,dfn[maxn],low[maxn],ori[maxn],sta[maxn],siz[maxn];
int rd()
{
char c;int x=0;c=getchar();while(c<'0'||'9'<c)c=getchar();
while('0'<=c&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();return x;
}
void add(int u1,int v1){nx[++ecnt]=info[u1];info[u1]=ecnt;v[ecnt]=v1;}
void dfs(int x)
{
sta[++en]=x;dfn[x]=low[x]=++id;
for(int e=info[x];e;e=nx[e])if(!dfn[v[e]])
{
dfs(v[e]),low[x]=min(low[x],low[v[e]]);
if(low[v[e]]>=dfn[x])
{
if(sta[en]==v[e]){if(k)k--,ans++;en--;}
else {cnt++; do siz[cnt]++;while(sta[en--]!=v[e]);}
}
}
else low[x]=min(low[x],dfn[v[e]]);
}
int main()
{
n=rd();m=rd();k=rd();
for(int i=1,u1,v1;i<=m;i++)u1=rd(),v1=rd(),add(u1,v1),add(v1,u1);
for(int i=1;i<=n;i++)if(!dfn[i])dfs(i),ans++;
sort(siz+1,siz+1+cnt);
for(int i=cnt,tmp;i>=1&&k;i--)k--,tmp=min(siz[i],k),ans+=tmp,k-=tmp;
printf("%d\n",ans);
}