【2021noip模拟赛day3】B. 仙人之环
【题意】
给出一个n个点,m条边的沙漠,你可以删去其中的k条边。求能分成的连通块数量最大值。
【分析】
很显然,我们优先删去割边,每个割边会带来1的贡献
如果k大于割边的个数,也就是删去所有割边后还有剩余操作次数
现在图已经成为若干个环了,也就是点双,第一次割一个环没有贡献,剩下的每次贡献为1
所以我们贪心的去从点数多的环开始割就行了,利用桶排,时间复杂度O(n)
【代码】
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e6+5; int head[maxn],tot; int n,m,k; inline int read() { int x=0,f=1; char cc=getchar(); while(cc!='-' && (cc<'0' || cc>'9')) cc=getchar(); if(cc=='-') f=-1,cc=getchar(); while(cc>='0' && cc<='9') { x=x*10+cc-'0'; cc=getchar(); } return x*f; } struct edge { int to,nxt; }e[maxn<<2]; void add(int x,int y) { e[++tot].to=y; e[tot].nxt=head[x]; head[x]=tot; } int dfn[maxn],low[maxn],dfstime; int ans=0,cir,val[maxn],siz[maxn],q[maxn],top; void tarjan(int u) { dfn[u]=low[u]=++dfstime; q[++top]=u; // if(u==9) printf("%d %d\n",dfn[u],low[u]); for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; // printf("%d %d\n",to,dfn[to]); if(!dfn[to]) { tarjan(to); low[u]=min(low[u],low[to]); if(low[to]>=dfn[u]) { if(q[top]==to) { if(k) k--,ans++; top--; } else { cir++; // siz[cir]=1; do { siz[cir]++; }while(q[top--]!=to); } } } else low[u]=min(low[u],dfn[to]); } } int main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++) { int x=read(),y=read(); add(x,y); add(y,x); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i),ans++; for(int i=1;i<=cir;i++) val[siz[i]]++; for(int i=n;i>=1;i--) { if(!k) break; for(int j=1;j<=val[i];j++) { if(!k) break; k--; int delta=min(k,i); k-=delta; ans+=delta; } if(!k) break; } printf("%d\n",ans); return 0; }