Description
在一个n*n的棋盘上,每个格子要么是黑色,要么是白色
有两个人在博弈,每次操作者可以选择一个白色格子,和一个不超过k的正整数a,然后将以这个格子为右下角的边长为a的正方形反色,需要保证这个正方形不越界,无法操作者输
问先手是否必胜
白色的格子为给出的m个矩形的并
k,n<=1e9,m<=5e5
Solution
考虑另一个游戏,每个格子有若干个koishi,每次操作可以选择一个koishi拿掉,然后将这个正方形内其他格子放上一个koishi
容易发现胜负只和某个格子内的koishi的奇偶性有关,这个和原问题是等价的
考虑每个koishi是独立的,我们只需要计算出Sg(i,j)表示koishi在点(i,j)的状态
打表发现Sg(i,j)=min(lowbit(i),lowbit(j),lim),其中lim是最大小于k的2的幂
证明可以归纳
然后考虑扫描线+线段树,我们在线段树上维护被覆盖的y的lowbit(y)=i的奇偶性,这可以写成一个30位二进制数
考虑记lowbit(y)>=i,那么可以发现一个被覆盖的区间[l,r]的二进制数就是r^(l-1)
用标记永久化的线段树维护即可
复杂度O(m log n)
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int read() {
char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
int x=ch-'0';
for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
const int N=1e6+5;
int n,m,k,c,tot,pl,pr,pz;
struct L{int x,l,r,opt;}a[N];
bool cmp(L a,L b) {return a.x<b.x;}
int tr[N*15],cnt[N*15],ls[N*15],rs[N*15],rt,tmp;
void modify(int &v,int l,int r) {
if (!v) v=++tmp;
if (pl<=l&&r<=pr) {
cnt[v]+=pz;
tr[v]=cnt[v]?(r^(l-1)):(tr[ls[v]]^tr[rs[v]]);
return;
}
int mid=l+r>>1;
if (pl<=mid) modify(ls[v],l,mid);
if (pr>mid) modify(rs[v],mid+1,r);
tr[v]=cnt[v]?(r^(l-1)):(tr[ls[v]]^tr[rs[v]]);
}
int main() {
freopen("invert.in","r",stdin);
freopen("invert.out","w",stdout);
n=read();m=read();k=read();
fo(i,1,m) {
int ax=read(),ay=read(),bx=read(),by=read();
a[++tot].x=ax;a[tot].l=ay;a[tot].r=by;a[tot].opt=1;
a[++tot].x=bx+1;a[tot].l=ay;a[tot].r=by;a[tot].opt=-1;
}
sort(a+1,a+tot+1,cmp);
int la=1,nim=0;
for(int l=1,r=0;l<=tot;l=r+1) {
while (r<tot&&a[r+1].x==a[l].x) r++;
nim^=((la-1)^(a[l].x-1))&tr[1];
fo(i,l,r) {
pl=a[i].l;pr=a[i].r;pz=a[i].opt;
modify(rt,1,n);
}
la=a[l].x;
}
for(c=1;c<=k;c<<=1);
puts(nim&(c-1)?"Gator":"Alligator");
return 0;
}