【CF461D】Appleman and Complicated Task

题目

题目链接:https://codeforces.com/problemset/problem/461/D
给定一个 \(n\times n\) 的网格,其中 \(k\) 个位置已经被填充为 O 或 X。
求有多少种填充剩下的位置为 O 或 X 的方式,使得任意位置四周的 O 的数量均为偶数。
答案对 \(1000000007\) 取模。
\(n,k\leq 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)\) 的网格会被第一行若干连续奇偶相同的网格贡献。具体的,会被第一行 \(j\in[|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;
}
posted @ 2021-01-18 15:27  stoorz  阅读(55)  评论(0编辑  收藏  举报