nikkei2019_2_qual_f Mirror Frame

nikkei2019_2_qual_f Mirror Frame

https://atcoder.jp/contests/nikkei2019-2-qual/tasks/nikkei2019_2_qual_f

在二维平面内,有一个正方形区域,顶点分别为 ((0,0),(0,N),(N,0),(N,N)) .

光线可以在这个区域内反射,且满足入射角等于出射角.

对于严格在区域内的点 ((x,y),0 < x < N, 0 < y < N) ,定义它的路径为

  • ((x,y)) 出发,向 ((x+1,y+1),(x-1,y-1),(x+1,y-1),(x-1,y+1)) 射出的四条光线的路径的并集

对于每个严格在区域内的位置,存在一个灯泡,初始状态为打开或者关闭.

每次可以选择一个严格在区域内的点,然后翻转所有在它的路径上的灯泡的状态.

现在有一些灯泡的初始状态还未确定,问有多少种确定这些灯泡的方案,满足最后存在一种操作序列使得所有灯泡关闭.

答案对 (998244353) 取模

(2 le N le 1500)

Tutorial

考虑如何判断一种状态是否合法

首先,沿对角线对称的灯泡的状态一定要相等.

一个灯泡的路径上所有的灯的坐标的 (x+y) 奇偶性相等,所以将按 (x+y) 分类考虑

现在问题可以转化为

(n) 个点的完全图,每条边有边权 (0)(1) .每次可以选择两个点,并翻转所有至少和这两个点中的一个相邻的边的状态.

问是否可以将所有边的边权变为 (0)

如果我们找到一个回路 ((x_1,x_2,cdots,x_k=x_1)) ,并依次翻转 ((x_1,x_2),(x_2,x_3),cdots,(x_{k-1},x_k)) ,那么状态改变的边就是 ((x_1,x_2),(x_2,x_3),cdots,(x_{k-1},x_k)) .也就是说,存在合法方案的充分条件为图中的 (1) 边存在欧拉回路.

假如 (n) 为偶数,那么如果我们翻转两个点 ((a,b)) ,那么 (a,b) 相邻的 (1) 边度数的奇偶性发生变化,所以可以通过操作使得所有点的度数为偶数.所以当 (n) 为偶数的时候一定合法.

假如 (n) 为奇数,那么翻转两个点 ((a,b)) 时, (a,b) 的度数奇偶性不会发生变化(同时证明了必要性),所以必须要满足每个点的相邻的 (1) 边数为偶数.

考虑求方案

(n) 个点((n) 为奇数)的完全图,每条边的边权 (0)(1)(-1), 对于所有的 (-1) 边分配 (0)(1) 的边权,使得所有点相邻的 (1) 边数为偶数

对于每个 (-1) 边联通块, (1) 边度数为奇数的点必须是偶数个,否则无解.

方案数就是 (-1) 边数减去树边个数((n) 减去联通块个数)

Code

#include <cmath> 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
template<class T> void rd(T &x) {
	x=0; int f=1,ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=getchar();}
	x*=f;
}
typedef long long ll;
const int mod=998244353;
const int maxn=1500+5;
int n; char A[maxn][maxn];
int head[maxn];
int adj[maxn][maxn],deg[maxn];
int vis[maxn],E,P,C;
struct edge {
	int to,nex;
	edge(int to=0,int nex=0):to(to),nex(nex){}
};
vector<edge> G;
inline int add(int x) {return x>=mod?x-mod:x;}
ll power(ll x,ll y) {
	ll re=1;
	while(y) {
		if(y&1) re=re*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return re;
}
inline void addedge(int u,int v) {
	G.push_back(edge(v,head[u])),head[u]=G.size()-1;
	G.push_back(edge(u,head[v])),head[v]=G.size()-1;
}
inline int idx0(int x,int y) {return x+y;}
inline int idx1(int x,int y) {return x<y?idx0(0,y-x):idx0(x-y,0);}
inline bool upd(int x,int y,char c) {
	if(c=='?') return 1;
	int a=x+y,b=x-y;
	if(a>n) a=2*n-a;
	if(b<0) b=-b;
	x=(a+b)/2,y=a-x;
	a=idx0(x,y),b=idx1(x,y);
	if(a>b) swap(a,b);
	if(adj[a][b]==-1) {
		adj[a][b]=c=='x'; 
		deg[a]^=adj[a][b];
		deg[b]^=adj[a][b];
	}
	else return adj[a][b]==(c=='x');
	return 1;
}
void travel(int u) {
	if(vis[u]) return; vis[u]=1;
	++P; if(deg[u]) ++C;
	for(int i=head[u];~i;i=G[i].nex) {
		int v=G[i].to;
		++E;
		travel(v);
	}
}
int sol(int k) {
	for(int i=1;i<n;++i) for(int j=1;j<n;++j) if(((i+j)&1)==k) {
		if(!upd(i,j,A[i][j])) return 0;
	}
	int cnt=0;
	for(int i=k;i<=n;i+=2) ++cnt;
	if(~cnt&1) {
		int an=1;
		for(int i=k;i<=n;i+=2) for(int j=i+2;j<=n;j+=2) {
			if(adj[i][j]==-1) an=add(an<<1);
		}
		return an;
	}
	for(int i=k;i<=n;i+=2) for(int j=i+2;j<=n;j+=2) {
		if(adj[i][j]==-1) addedge(i,j);
	}
	int an=1;
	for(int i=k;i<=n;i+=2) if(!vis[i]) {
		E=0,P=0,C=0,travel(i),E>>=1;
		if(C&1) return 0;
		an=an*power(2,E-(P-1))%mod;
	}
	return an;
}
int main() {
	rd(n);
	for(int i=1;i<n;++i) scanf("%s",A[i]+1);
	memset(adj,-1,sizeof(adj));
	memset(head,-1,sizeof(head));
	printf("%lld
",(ll)sol(0)*sol(1)%mod);
	return 0;
}
原文地址:https://www.cnblogs.com/ljzalc1022/p/13266208.html