【二分图上的构造】codeforces173D

深入探讨图论问题,详细解析codeforces173D题目。通过分析,揭示二分图染色的特殊情况处理,如点数模2余数为0时的简单情况,以及一边多一个点、一边多两个点时的解决方案。通过度数判断和图结构转化,解决当某一边多点时如何保持合法染色的问题,涉及图的森林结构和树的性质。最后讨论了无解的情况,确保时间复杂度为O(n+m)。
摘要由CSDN通过智能技术生成

好久没这么深入地分析一道图论题了,感觉酣畅淋漓,虽然考场上花的时间比较久,但是好在分析得十分透彻,直接一Y了

考虑如果尽量把一种颜色分在同一边,那么肯定是合法的,如果说两边点数模2都余0,那就皆大欢喜了;唯一特殊的情况是一边多一个点,一边多2个点,由于所有颜色都是等价的,因此只需一边找一个点,另一边找两个与他不相邻的两个点即可满足要求,这个根据度数可以轻松判出;如果不存在这种情况的话,假设x部是多一个点,y部是多两个点,此时x部的点都肯定顶多只能在y中找到一个与它不相邻的点,这样子的话,原图就化为了一个由只有一层的树构成的森林,而且父亲在y部,儿子在x部,如果有两棵以上的树点数大于2,那么一开始剩的一种颜色就可以把要放在y部的一个点换到x部来,那个x部被挤出来的一种颜色有可以放到另一棵树中的y部去;如果这种情况也不满足的话,那么这个图就更加特殊了,顶多存在一棵超过两个点的树以及一系列只有一条边的两对点构成,显然剩下的那种颜色即便填进来,挤出去的颜色也没地方放,那么就是无解。时间复杂度是严格o(n+m)的。

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
const int oo=1073741819;
using namespace std;
int n,m,ss,u[2][200000],d[200000],v[200000],k;
int tail[2000000],next[2000000],sora[2000000];
int s[2],du[200000],ans[200000],st[200000];
void getout0()
{
    printf("YES\n");
    for (int i=1;i<=s[0];i+=3,k--)
        for (int j=i;j<=i+2;j++) ans[u[0][j]]=k;
    for (int i=1;i<=s[1];i+=3,k--)
        for (int j=i;j<=i+2;j++) ans[u[1][j]]=k;
    printf("%d",ans[1]);
    for (int i=2;i<=n;i++) printf(" %d",ans[i]);
    printf("\n");
}
void getout1(int e,int x)
{
    printf("YES\n");
    memset(v,0,sizeof(v));
    for (int i=x,ne;next[i];) {
        i=next[i],ne=sora[i];
        v[ne]=1;
    }
    ans[x]=k;
    for (int i=1,cnt=1;i<=s[e^1] && cnt<=2;i++) {
        int ne=u[e^1][i];
        if (!v[ne]) ans[ne]=k,cnt++;
    }
    k--;
    for (int i=1,cnt=1;i<=s[0];i++) {
        int ne=u[0][i];
        if (ans[ne]) continue;
        ans[ne]=k;
        cnt++;
        if (cnt>3) k--,cnt=1;
    }
    for (int i=1,cnt=1;i<=s[1];i++) {
        int ne=u[1][i];
        if (ans[ne]) continue;
        ans[ne]=k;
        cnt++;
        if (cnt>3) k--,cnt=1;
    }
    printf("%d",ans[1]);
    for (int i=2;i<=n;i++) printf(" %d",ans[i]);
    printf("\n");
}
void getout2(int e,int x,int y)
{
    printf("YES\n");
    memset(v,0,sizeof(v));
    for (int i=x,ne;next[i];) {
        i=next[i],ne=sora[i];
        v[ne]=1;
    }
    ans[x]=k;
    for (int i=1,cnt=1;i<=s[e^1] && cnt<=2;i++) {
        int ne=u[e^1][i];
        if (!v[ne]) ans[ne]=k,cnt++;
    }
    k--;
    memset(v,0,sizeof(v));
    x=y;
    for (int i=x,ne;next[i];) {
        i=next[i],ne=sora[i];
        v[ne]=1;
    }
    ans[x]=k;
    for (int i=1,cnt=1;i<=s[e^1] && cnt<=2;i++) {
        int ne=u[e^1][i];
        if (!v[ne] && !ans[ne]) ans[ne]=k,cnt++;
    }
    k--;
    for (int i=1,cnt=1;i<=s[0];i++) {
        int ne=u[0][i];
        if (ans[ne]) continue;
        ans[ne]=k;
        cnt++;
        if (cnt>3) k--,cnt=1;
    }
    for (int i=1,cnt=1;i<=s[1];i++) {
        int ne=u[1][i];
        if (ans[ne]) continue;
        ans[ne]=k;
        cnt++;
        if (cnt>3) k--,cnt=1;
    }
    printf("%d",ans[1]);
    for (int i=2;i<=n;i++) printf(" %d",ans[i]);
    printf("\n");    
}
void origin()
{
    ss=n;
    for (int i=1;i<=n;i++) tail[i]=i;
}
void link(int x,int y)
{
    du[x]++,du[y]++;
    ++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y;
    ++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x;
}
void bfs(int s1)
{
    int h,r,ne,na;
    h=r=0;
    st[r=1]=s1,d[s1]=0;
    for (;h<r;) {
        ne=st[++h];
        for (int i=ne;next[i];) {
            i=next[i],na=sora[i];
            if (d[na]>oo) {
                d[na]=d[ne]^1;
                st[++r]=na;
            }
        }
    }
    for (int i=1;i<=r;i++) {
        ne=st[i];
        int e=(d[ne]&1);
        u[e][++s[e]]=ne;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    origin();
    for (int i=1;i<=m;i++) {
        int x,y;
        scanf("%d%d\n",&x,&y);
        link(x,y);
    }
    memset(d,127,sizeof(d));
    s[0]=s[1]=0;
    for (int i=1;i<=n;i++)
        if (d[i]>oo) bfs(i);
    k=n/3;
    if ((s[0]%3==0) && (s[1]%3==0)) {
        getout0();
        return 0;
    }
    int e;
    if ((s[0]%3==1)) e=0;
    else e=1;
//    memset(v,0,sizeof(v));
    int t=0;
    for (int i=1;i<=s[e];i++) {
        int ne=u[e][i];
        if (s[e^1]-du[ne]>=2) {
            t=ne;
            break;
        }
    }
    if (t) {
        getout1(e,t);
        return 0;
    }
    int p=0;
    e^=1;
    for (int i=1;i<=s[e];i++) {
        int ne=u[e][i];
        if (s[e^1]-du[ne]>=2) {
            if (!t) t=ne;
            else p=ne;
        }
    }
    if ((t) && (p)) {
        getout2(e,t,p);
        return 0;
    }
    printf("NO\n");
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值