【CF461D】Appleman and Complicated Task

题目

题目链接:https://codeforces.com/problemset/problem/461/D
给定一个 (n imes n) 的网格,其中 (k) 个位置已经被填充为 O 或 X。
求有多少种填充剩下的位置为 O 或 X 的方式,使得任意位置四周的 O 的数量均为偶数。
答案对 (1000000007) 取模。
(n,kleq 10^5)

思路

挺巧妙的一道题。下文所有下标均减一,即范围在 ([0,n)) 内。
把 O 看做 (1),X 看做 (0),因为四周的格子异或起来等于 (0),所以有 (a_{i,j}=a_{i-1,j-1}mathrm{ xor }a_{i-1,j+1}mathrm{ xor }a_{i-2,j})
一直这样不断往上算,画个图可以发现,最终位置 ((x,y)) 的网格会被第一行若干连续奇偶相同的网格贡献。具体的,会被第一行 (jin[|x-y|,min(2n-x-y,x+y)])(j)(x+y) 奇偶性相同的格子 ((1,j)) 贡献到。
第一行的格子做一下前缀异或和,一个位置 ((x,y)) 被强制填 (p) 其实等价于 (a_{1,|x-y|-2}mathrm{ xor }a_{1,min(2n-x-y,x+y)}=p)
那么现在题意转化为有若干个关于异或的限制,求异或查分后本质不同的序列数量。那么就在 (|x-y|-2)(1,min(2n-x-y,x+y)) 之间连一条边,然后 dfs 求联通块数量即可。
最终答案要除以 (4),因为 (-1)(-2) 是我们虚构出来的格子,它们任意填数的 (4) 种方案都应该算 (1) 种。
时间复杂度 (O(n+m))

代码

#include <bits/stdc++.h>
using namespace std;

const int N=100010,MOD=1000000007;
int n,m,tot,ans,col[N],head[N];
char ch[3];

struct edge
{
	int next,to,dis;
}e[N*2];

void add(int from,int to,int dis)
{
	e[++tot]=(edge){head[from],to,dis};
	head[from]=tot;
}

bool dfs(int x,int c)
{
	if (col[x] && col[x]==c) return 1;
	if (col[x] && col[x]!=c) return 0;
	col[x]=c;
	for (int i=head[x];~i;i=e[i].next)
		if (!dfs(e[i].to,e[i].dis^c)) return 0;
	return 1;
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	n--;
	for (int i=1,x,y;i<=m;i++)
	{
		scanf("%d%d%s",&x,&y,ch);
		x--; y--;
		if (x+y>n)
		{
			add(abs(x-y),2*n-x-y+2,ch[0]=='o');
			add(2*n-x-y+2,abs(x-y),ch[0]=='o');
		}
		else
		{
			add(abs(x-y),x+y+2,ch[0]=='o');
			add(x+y+2,abs(x-y),ch[0]=='o');
		}
	}
	ans=1;
	for (int i=0;i<=n+2;i++)
		if (!col[i])
		{
			if (!dfs(i,2)) return printf("0"),0;
			ans=ans*2%MOD;
		}
	printf("%lld",250000002LL*ans%MOD);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14292937.html