Codeforces Round #544 (Div. 3) F2. Spanning Tree with One Fixed Degree (构造)

链接

题意:给出一个图,构造一个点1的度为d的生成树

一开始就先判断下点1的度是否大于等于d,不够的话肯定是构造不了的。
先不管点1,先对剩下的点根据连通性分下块。这时候这些块都至少与点1有一条边,一个块里可能有几个点与点1有边,随便先连一个就可以了。这时候如果有一个块没有一个点与点1有边,或者连的边数已经大于d,也是构造不了了。如果边数还不够d,那么就再选一些没跟点1连过的点且与点1有边的点跟点1相连,直到够d了为止,最后按照普通的生成树做法,注意这时不能再加入新的有端点是1的边了。

根据连通性分成块的好处是在块中随便选点与点1相连,还可以保证最后可能有边使得块内剩下的与整个图连通。

参考代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,d;
const int N=2e5+5;
int head[N],cnt;

struct node{
    int u,v,nxt;
}edge[N<<1];

vector<int>kuai[N],cd;
vector<pair<int,int> >ans;
int top;
int vis[N];
bool conn[N];
int fa[N];

int findfa(int x){
    return fa[x]=fa[x]==x?x:findfa(fa[x]);
}
void addedge(int u,int v){
    edge[++cnt].v=v;
    edge[cnt].u=u;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
}

void dfs(int u){//根据连通性分块
    vis[u]=top;
    kuai[top].push_back(u);
    for(int i=head[u];~i;i=edge[i].nxt){
        int v=edge[i].v;
        if(v==1||vis[v])continue;
        dfs(v);
    }
}

int main(){
    scanf("%d%d%d",&n,&m,&d);
    memset(head,-1,sizeof(head));
    int tp=0;//一开始记录点1的度
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
        if(v==1||u==1)tp++;
    }

    if(tp<d){
        printf("NO\n");
        return 0;
    }

    for(int i=2;i<=n;i++){
        if(!vis[i]){
            top++;
            dfs(i);
        }
    }
    for(int i=head[1];~i;i=edge[i].nxt){//找一下与点1有边的点
        int v=edge[i].v;
        conn[v]=true;
        cd.push_back(v);
    }
    for(int i=1;i<=n;i++){
        fa[i]=i;
    }

    for(int i=1;i<=top;i++){//每个块找个点与点1相连
        bool ff=false;
        for(int j=0;j<kuai[i].size();j++){
            if(conn[kuai[i][j]]){
                ff=true;
                fa[kuai[i][j]]=1;
                ans.push_back(make_pair(1,kuai[i][j]));
                break;
            }
        }
        if(!ff){
            printf("NO\n");
            return 0;
        }
    }
    if(ans.size()>d){
        printf("NO\n");
        return 0;
    }

    d-=ans.size();
    for(int i=0;i<cd.size()&&d;i++){//补一下不够的边数
        int v=cd[i];
        if(fa[v]==1)continue;
        fa[v]=1;
        d--;
        ans.push_back(make_pair(1,v));
    }

    if(d!=0){
        printf("NO\n");
        return 0;
    }

    for(int i=1;i<=cnt;i++){
        int fa1=findfa(edge[i].u),fa2=findfa(edge[i].v);
        if(edge[i].u==1||edge[i].v==1)continue;
        if(fa1!=fa2){
            fa[fa1]=fa2;
            ans.push_back(make_pair(edge[i].u,edge[i].v));
        }
    }
    printf("YES\n");
    for(int i=0;i<ans.size();i++){
        printf("%d %d\n",ans[i].first,ans[i].second);
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值