[BZOJ2303][APIO2011]方格染色 异或+并查集

这题首先要推式子:
题目条件即为A(i-1,j-1) xor A(i-1,j) xor A(i,j-1) xor A(i,j)=1.
又有A(i-1,j-2) xor A(i-1,j-1) xor A(i,j-2) xor A(i,j-1)=1.
相邻两列两式异或得A(i-1,j-2)xor A(i,j-2) xor A(i-1,j) xor A(i,j)=0.
就这样一直递推过去,得A(i-1,1)xor A(i,1)xor A(i-1,j) xor A(i,j)=[j mod 2=0].
然后我们又有A(i-2,1) xor A(i-1,1) xor A(i-2,j) xor A(i-1,j)=[j mod 2=0].
就这样相邻两行不断异或,得A(1,1) xor A(i,1) xor A(1,j) xor A(i,j)=[(i mod 2=0)and(j mod 2=0)].
观察式子,我们发现如果A(1,1)确定,那么每一个已经涂好的颜色((x<>1)and(y<>1))都是对A(x,1)和A(1,y)的一个限制。于是我们枚举A(1,1)填0或1(当然如果A(1,1)事先被限制就只要一种)对第一行和第一列开并查集,用并查集维护限制。
!!!并查集维护异或限制!!!:若x xor y=0,就merge(x,y);merge(x’,y’)。若x xor y=1,就merge(x,y’);merge(x’,y)。
最后并查集会有很多联通块,记块数为sum。首先如果有x’和x在一个联通块中,则无解。接下来对于每一个第一行和第一列中已经被涂好的颜色,都使得一对连通块确定(即必须选其中一个而不能选另一个),我们记一个choose[root]=true,root为该连通块的编号,若choose[getfa(x)]和choose[getfa(x’)]同为true,则无解退出。否则sum=sum-2.
代码中是记得一个visit[].
最终答案为2^(sum/2),即每一对未确定的连通块都有两种选法。
最后把A(1,1)的两种情况的答案相加即可。

代码:

const
  maxn=1000100;
  md=1000000000;
type
  node=record
    x,y:longint;
    c:-1..1;
  end;

var
  n,m,k,i,c1_1:longint;
  ans:int64;
  r:array[0..maxn]of node;
  fa:array[0..4*maxn]of longint;
  visit,chs:array[0..4*maxn]of boolean;
function getfa(v:longint):longint;
begin
  if fa[v]=v then exit(v);
  fa[v]:=getfa(fa[v]);
  exit(fa[v]);
end;
procedure merge(x,y:longint);
begin
  if getfa(x)<>getfa(y) then fa[getfa(x)]:=getfa(y);
end;
function ksm(a:int64;b:longint):int64;
begin
  ksm:=1;
  while b>0 do
  begin
    if b mod 2=1 then ksm:=ksm*a mod md;
    a:=a*a mod md;
    b:=b div 2;
  end;
end;

procedure solve(flag:longint);
var
  o,sum:longint;
begin
  for i:=1 to 2*(n+m) do
    fa[i]:=i;
  for i:=1 to k do
    with r[i] do
    begin
      if (x=1)or(y=1) then continue;
      o:=c xor flag;
      if (x mod 2=0)and(y mod 2=0) then o:=o xor 1;
      if o=0 then begin merge(2*x,2*(n+y)); merge(2*x-1,2*(n+y)-1); end
      else begin merge(2*x,2*(n+y)-1); merge(2*x-1,2*(n+y)); end;
    end;
  sum:=0;
  for i:=1 to n+m do
  begin
    if getfa(2*i)=getfa(2*i-1) then exit;
    visit[getfa(2*i)]:=true;
    visit[getfa(2*i-1)]:=true;
  end;
  for i:=1 to 2*(n+m) do
    if visit[i]=true then inc(sum);
  sum:=sum div 2-2;
  visit[1]:=false;
  for i:=1 to k do
    with r[i] do
    begin
      if (x=1)and(y<>1)and(visit[getfa(2*(n+y)-c)]=true) then
      begin
        visit[getfa(2*(n+y)-c)]:=false;
        if visit[getfa(2*(n+y)-(c xor 1))]=false then exit;
        dec(sum);
      end;
      if (x<>1)and(y=1)and(visit[getfa(2*x-c)]=true) then
      begin
        visit[getfa(2*x-c)]:=false;
        if visit[getfa(2*x-(c xor 1))]=false then exit;
        dec(sum);
      end;
    end;
  ans:=(ans+ksm(2,sum))mod md;
end;

procedure work;
begin
  fillchar(visit,sizeof(visit),false);
  if c1_1<>1 then solve(0);
  fillchar(visit,sizeof(visit),false);
  if c1_1<>0 then solve(1);
end;
begin
  readln(n,m,k);
  c1_1:=-1;
  for i:=1 to k do
  begin
    readln(r[i].x,r[i].y,r[i].c);
    if (r[i].x=1)and(r[i].y=1) then c1_1:=r[i].c;
  end;
  work;
  writeln(ans);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值