[YZOJ]P2966[校内训练20170321]染色

YZOJ

题意分析
有一个有向图,要求对点染色为黑或白,满足每一个点的出边连接的与这个点颜色相同的点个数为偶数个。

题解

可行性
本题满足多元关系,即一个点的染色会影响到其他点。
存在环,网络流不好做,所以考虑构造方程组。

条件
一个点已知信息为出边的条数,考虑一组可行解满足的关系。设任意一个点出边
1.当一个点出边条数为奇数时:
1)若该点染色为1:出边到达的点染色为1的为偶数(染色为0的为奇数)。
2)若该点染色为0:出边到达的点染色为0的为偶数(染色为1的为奇数)。
2.当一个点出边条数为偶数时:
1)若该点染色为1:出边到达的点染色为1的为偶数(染色为0的为偶数)。
2)若该点染色为0:出边到达的点染色为0的为偶数(染色为1的为偶数)。

考虑当前方程可行条件:条件是奇偶性,考虑异或方程。发现:
1)出边条数为奇数时,原点和出边到达的点异或和为1。
2)出边条数为奇数时,出边到达的点异或和为0。

建模方法
若存在 i->j 则把矩阵第 i 行第 j 列设为 1。
若 i 出边为奇数,则把矩阵第 i 行第 i 列设为 1。

优化
复杂度 O(n3) 109 级别,用bitset优化,常数将为1/32。

bitset

//头文件。
#include<bitset>


//定义。 
bitset<2005>a;
bitset<2005>b[2005]; 


//函数。
b.size()//b中二进制位的个数

b[pos]//访问b中在pos处的二进制位

b.test(pos)//b中在pos处的二进制位是否为1

b.set()//把b中所有二进制位都置为1

b.set(pos)//把b中在pos处的二进制位置为1

b.reset()//把b中所有二进制位都置为0

b.reset(pos)//把b中在pos处的二进制位置为0

b.flip()//把b中所有二进制位逐位取反

b.flip(pos)//把b中在pos处的二进制位取反

b.count()//返回这个bitset中1的个数

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<bitset>
#define R register
using namespace std;

void read(int &aa)
{
    R char ch;while(ch=getchar(),ch<'0'||ch>'9');aa=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9')aa=aa*10+ch-'0';
}

int N,b[2010],xx[2010],cnt=0;
bitset<2005>a[2005];

int main()
{
    R int i,j,k;
    for(i=1,read(N);i<=N;++i)
    {
        read(j);
        if(j&1)a[i].set(i),b[i]=1;
        while(j--){read(k);a[i].set(k);}
    }

    R int col,m,x;
    for(i=1,col=1;col<=N;++i)
    {
        k=i;
        for(j=i;j<=N;++j)
            if(a[j].test(col))
                {k=j;break;}
        if(k!=i)
            swap(a[i],a[k]),swap(b[i],b[k]);
        if(!a[i].test(col))
            {++col,--i;continue;}
        for(j=i+1;j<=N;++j)
            if(a[j].test(col))
                a[j]^=a[i],b[j]^=b[i];
        ++col;
    }
    for(i=N;i>=1;--i)
    {
        m=0;
        for(j=i+1;j<=N;++j)
            m=m^(a[i][j]*xx[j]);
        xx[i]=b[i]^m;
        if(xx[i]&&!a[i].test(i))
        {
            printf("Impossible");
            return 0;
        }
        if(xx[i])cnt++;
    }
    printf("%d\n",cnt);
    for(i=1;i<=N;i++)if(xx[i])printf("%d ",i);
    return 0;
}  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值