您的位置:首页 > 其它

【JZOJ3834】【NOI2015模拟9.14】【CF461D】Complicated Task(异或方程组+并查集)

2018-02-05 22:03 267 查看

Problem



Input



Output



Hint

对于30%的数据,N<=15

对于100%的数据,N,K<=10^5

Solution

  若设点(i,j)的颜色为c[i][j](’x’为0,’o’为1),对于棋盘中的每个点(i,j),都有异或方程c[i-1][j]^c[i][j-1]^c[i][j+1]^c[i+1][j]=0。

  那么考虑对于每个点(i+1,j),c[i+1][j]就等于c[i-1][j]^c[i][j-1]^c[i][j+1]。

  这样可以做个DP,再在外面套个暴力,枚举第一行的状态。不过只有30分。

  那么我们套路一波,推一推第一行的点对棋盘中每个点的影响。

  比如说,对于c[i][j],它等于c[i-2][j]^c[i-1][j-1]^c[i-1][j+1],而c[i-1][j-1]又等于c[i-3][j-1]^c[i-2][j-2]^c[i-2][j],c[i-1][j+1]也可以用类似这样的方法表示,那么c[i-2][j]会被算3次,消掉2次,再拿它往上带。这样不断地回代,我们可以发现,c[i][j]的取值取决于第一行连续的一段列数为奇数或为偶数的点的颜色,具体地说,就是从点(i,j)向上画两条射线,以x轴正方向为0°,右边一条为45°,左边一条为135°,这两条射线投影在第一行上。

  如果我们把棋盘重标号为[0..N-1],那么我们可以通过找规律得知,对于一个点(x,y),它在第0行上的投影的左端点l=abs(a-b),右端点r=2*(n-1)-a-b。

  这样的话,我们就可以求出对于那K个给定的点,它在第0行上的投影。通俗地说,设第0行上的点的颜色为a[0],a[1],…,a[N-1],则对于某个给定的点(x,y),c[x][y]可以表示为a[l]^a[l+2]^a[l+4]^…^a[r]。

  这样我们就得到了K个异或方程。但请别一看见方程就想着高斯消元,别忘了数据范围:K<=10^5。

  然而此题并不需要我们求出一种方案,所以我们可以另辟蹊径解决这个难题。

  我们可以假设有异或前缀和s[i],表示a[0/1]^a[2/3]^a[4/5]^…^a[i-2]。这么写是因为我们这个前缀和还需要分奇偶。譬如s[5],5是个奇数,所以s[5]=a[1]^a[3]。

  那么,上述方程均可表示为s[l]^s[r+2]的形式。

  这样的话,如何判断其可否有解呢?

  我们可以把每个前缀和拆开成两个点,0和1,分别表示此前缀和为0和为1的情况。对于K个点中的某点,若其颜色为0,则s[l]与s[r+2]的值相同,所以从s[l][0]向s[r+2][0]连一条边,s[l][1]向s[r+2][1]连一条边;若其颜色为1,则s[l]与s[r+2]的值不同,所以从s[l][0]向s[r+2][1]连一条边,s[l][1]向s[r+2][0]连一条边。

  对于连边,我们可以用并查集。如果有某次连边构成了环,则必然有某个x,它的s[x][0]和s[x][1]之间有一条边,那么意思就是s[x][0]的值和s[x][1]的值相同,0和1相同,显然不合法。于是直接输出方案数0,return。

  如果没有的话,我们可以求出所有的联通块个数。显然s[0][0]、s[0][1]、s[1][0]、s[1][1]这4个点的颜色都是已知的,所以它们所在的联通块中的所有点的颜色也均为已知,所以将联通块个数-4;而又因为我们得出的联通块必然是对称的,那假如有两条边从s[2][0]连向s[4][1],从s[2][1]连向s[4][0],我们又不可能既将s[2]定为0、s[4]定为1,又将s[2]定为1、s[4]定为0,所以减完4还要再/2。

  然后我们就得出未知的、本质不同的联通块个数cnt,也即高斯消元中的自由元个数了。我们对于其中一个联通块,都可以给其赋2个值,于是答案即2cnt2cnt。

  时间复杂度:O(n+kα(n))O(n+kα(n))。

Code

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 200100
#define M 1000000007
#define fo(i,a,b) for(i=a;i<=b;i++)
int i,n,k,x,y,l,r,fat
,cnt;
char ch[1];
int gef(int x)
{
return fat[x]?fat[x]=gef(fat[x]):x;
}
inline bool uni(int x,int y)
{
int fx=gef(x),fy=gef(y);
if(g
c213
ef(fx^1)==fy||gef(fy^1)==fx)return 0;
if(fx!=fy)fat[fx]=fy;
return 1;
}
inline bool check(int l,int r,int p)
{
return p?uni(2*l,2*r+1)&&uni(2*l+1,2*r):uni(2*l,2*r)&&uni(2*l+1,2*r+1);
}
int pow2(int x)
{
int ans=1;
fo(i,1,x)ans=ans*2%M;
return ans;
}
int main()
{
scanf("%d%d",&n,&k);
fo(i,1,k)
{
scanf("%d%d%s",&x,&y,&ch);
x--;
y--;
l=abs(x-y);
r=min(x+y,2*(n-1)-x-y);
if(!check(l,r+2,ch[0]=='o'))
{
printf("0\n");
return 0;
}
}
fo(i,0,2*n+3)cnt+=(gef(i)==i);
printf("%d",pow2((cnt-4)/2));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: