【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;
}

 

posted @ 2021-09-19 22:31  andyc_03  阅读(85)  评论(0编辑  收藏  举报