三色图

来源:互联网 发布:centos 防止端口扫描 编辑:程序博客网 时间:2024/04/25 07:56

题目大意

给定一个二分图,请你给每条边一个颜色(0,1,2)
一个点的点权定义为其所有与之相连的边颜色和模3
任意一条边连接的两端点点权必须不同
请构造一种染色方案

结论

对每一个联通块进行分析。
首先说出我们的构造目标:左边点点权均为0,右边点点权均不为0,这样一定符合要求
具体构造方案是将右边的点分为两两一组,以其中一个为起点,另一个为终点,然后跑一条可行路径,因为是联通的,所以该路径一定存在。对于路径中经过的边,按1、2、1、2的去加,也就是路径中第一条经过的边颜色+1,第二条+2,第三条+1,第四条+2,以此类推。
我们发现,这样做,起点点权+1,终点点权+2,其余所有点点权不变!
但是,因为要两两一组,如果右边点的个数是奇数怎么办?
我们可以找到右边一个点权为1的点,以它为起点,以这个剩余的点为终点,再做一次。不过,我们应该想到,如果右边只有1个点,那一对都凑不成了!
但是如果左边有超过1个点,即使右边只有1个点,我们可以左右交换!
而显然, 无解的情况是左右都只有1个点。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=3000+10,maxm=10000+10;int h[maxn],s[maxn],x[maxn],y[maxn],go[maxm*2],next[maxm*2],co[maxm];bool bz[maxn],pd[maxn];int i,j,k,l,t,n,n1,n2,m,tot,xx,yy;void add(int x,int y){    go[++tot]=y;    next[tot]=h[x];    h[x]=tot;}void dfs(int i,int c){    bz[i]=1;    if (!c) x[++xx]=i;else y[++yy]=i;    int t=h[i];    while (t){        if (!bz[go[t]]) dfs(go[t],1-c);        t=next[t];    }}bool dg(int x,int y,int c){    pd[x]=1;    if (x==y) return 1;    int t=h[x];    while (t){        if (!pd[go[t]]&&dg(go[t],y,1-c)){            (co[(t+1)/2]+=c+1)%=3;            return 1;        }        t=next[t];    }}void work(){    int i;    if (yy==1){        swap(x[1],y[1]);        fo(i,2,xx) y[i]=x[i];        swap(xx,yy);    }    fo(i,1,yy/2){        j=y[i*2-1];k=y[i*2];        fo(l,1,xx) pd[x[l]]=0;        fo(l,1,yy) pd[y[l]]=0;        dg(j,k,0);        s[j]=1;s[k]=2;    }    if (yy%2==1){        j=y[1];k=y[yy];        fo(l,1,xx) pd[x[l]]=0;        fo(l,1,yy) pd[y[l]]=0;        dg(j,k,0);    }}int main(){    scanf("%d%d%d",&n1,&n2,&m);    fo(i,1,m){        scanf("%d%d",&j,&k);        add(j,k+n1);add(k+n1,j);    }    fo(i,1,n1)        if (!bz[i]){            xx=yy=0;            dfs(i,0);            work();        }    printf("Yes\n");    fo(i,1,m) printf("%d ",co[i]);}
0 0