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;
}