Codeforces Round #375 (Div. 2) F. st-Spanning Tree

题目链接:点击打开链接

题意:给出无向图n个点,m条边 (2 ≤ n ≤ 200 0001 ≤ m ≤ min(400 000, n·(n - 1) / 2)

找出一个生成树,使得s点的度不超过ds,t点的度不超过dt;

解:

数据分别是20w和40w,又是找生成树,显然是kurskal。

首先,我们先不管s和t点,把其他点弄成生成树再说。

然后,我们再考虑s点和t点与其他点的连边。

 最后,再考虑s和t之间的连边(贪心的策略)

要注意的问题是:在考虑s点和t点与其他点的连边是要判断是否s、t的度超过了ds、dt。因为,s可以连的点,t也可能连,那么谁去连呢?那就要看谁的度不超。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=2e5+5;
struct edge
{
    int en,next;
    edge(int en=0,int next=-1):
        en(en),next(next){}
};
struct edge E[MAXN*4],e[MAXN*4];
int p[MAXN],num,fa[MAXN];
inline void add(int st,int en)
{
    E[num]=edge(en,p[st]);
    p[st]=num++;
}
int findfa(int x)
{
    if(x==fa[x])return x;
    return fa[x]=findfa(fa[x]);
}
int main()
{
    int n,m,s,t,ds,dt,i,dus,dut;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(p,-1,sizeof(p));
        num=0;
        for(i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
            e[i]=edge(u,v);
        }
        scanf("%d%d%d%d",&s,&t,&ds,&dt);
        for(i=1;i<=n;i++)fa[i]=i;
        vector<edge>ans;
        for(i=1;i<=m;i++)
        {
            int tx=e[i].en;
            int ty=e[i].next;
            if(tx==s||tx==t||ty==s||ty==t)continue;

            tx=findfa(tx);ty=findfa(ty);
            if(tx!=ty)
            {
                fa[tx]=ty;
                ans.push_back(e[i]);
            }
        }
        dus=0,dut=0;
        for(i=p[s];i!=-1;i=E[i].next)
        {
            int en=E[i].en;
            if(en==t)continue;
            int tx=findfa(s);
            int ty=findfa(en);
            if(tx!=ty&&dus<ds)
            {
                dus++;
                fa[tx]=ty;
                ans.push_back(edge(s,en));
            }
        }
        for(i=p[t];i!=-1;i=E[i].next)
        {
            int en=E[i].en;
            if(en==s)continue;
            int tx=findfa(t);
            int ty=findfa(en);
            if(tx!=ty&&dut<dt)
            {
                dut++;
                fa[tx]=ty;
                ans.push_back(edge(t,en));
            }
        }
        for(i=p[s];i+1;i=E[i].next)
        {
            int en=E[i].en;
            if(en!=t)continue;
            int tx=findfa(s);
            int ty=findfa(t);
            if(tx!=ty)
            {
                fa[tx]=ty;
                dus++;
                dut++;
                ans.push_back(edge(s,t));
                break;
            }
        }
        if(dus>ds||dut>dt)
        {
            puts("No");
            continue;
        }
        int tx=findfa(s);
        bool yes=0;
        for(i=1;i<=n;i++)
        {
            int ty=findfa(i);
            if(tx!=ty)
            {
                yes=1;break;
            }
        }
        if(yes)
        {
            puts("No");
            continue;
        }
        puts("Yes");
        for(i=0;i<n-1;i++)
        {
            printf("%d %d\n",ans[i].en,ans[i].next);
        }

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值