正题
题目链接:https://ac.nowcoder.com/acm/contest/1101#question
题目大意
nnn个点mmm条边的沙漠(所有联通子图都是仙人掌),删除kkk个点使得剩下的连通块最多。
解题思路
对于图上的每条割边,删去之后就可以多出一个联通块,所以我们就可以先删去所有割边。
之后图上剩下许多个环,没两个环之间由一个割点连接,所以我们可以用缩点双联通分量的方法找出所有的环。
我们发现对于每个环删除一条边之后就可以变成一条链,也就是我们删除kkk条边就会多出k−1k-1k−1个联通块。然后我们可以对于所有的环按照大小排序,然后从大的到小的分成链。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N=1e6+10;
struct node{int to,next;
}a[N*4];
int n,m,k,tot=1,ans,c,cnt,num;
int dfn[N],low[N],ls[N],cir[N];
bool mark[N*4],flag;
stack<int> s;
void addl(int x,int y)
{a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;
}
void tarjan(int x,int edge)
{dfn[x]=low[x]=++cnt;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(!dfn[y]){tarjan(y,i);low[x]=min(low[x],low[y]);if(low[y]>dfn[x]){mark[i]=mark[i^1]=1;if(k) ans++,k--;}}else if(i!=(edge^1))low[x]=min(low[x],dfn[y]);}
}
void tarjan2(int x)
{dfn[x]=low[x]=++cnt;s.push(x);for(int i=ls[x];i;i=a[i].next){if(mark[i]) continue;int y=a[i].to;if(!dfn[y]){tarjan2(y);num=0;low[x]=min(low[x],low[y]);if(low[y]>=dfn[x]){int z;do{z=s.top();num++;s.pop();}while(z!=y);num++;cir[++c]=num;}}else low[x]=min(low[x],dfn[y]);}
}
int main()
{//freopen("2.in","r",stdin);scanf("%d%d%d",&n,&m,&k);for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,0),ans++;cnt=0;memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));for(int i=1;i<=n;i++)if(!dfn[i])tarjan2(i);sort(cir+1,cir+1+c);for(int i=c;i>=1;i--){if(k<2) break;ans+=min(k,cir[i])-1;k-=min(k,cir[i]);}printf("%d",ans);
}