原题链接:​​http://codeforces.com/problemset/problem/1076/G​

Description

考虑这样一个博弈
你有一个序列B,一开始有一个棋子在B的第一个位置。
双方轮流操作,第一次操作前将B[1]-1
游戏有一个参数m,操作以下面的形式进行

  • 假设当前棋子在位置x,当前操作的一方需要选择一个位置【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_#define,且B[y]>0,将棋子移到位置y,并将B[y]-1,之后将操作权给另一个人。
  • 不能操作的一方输掉游戏。

这道题目是这样的
给出一个长度为n的序列A,游戏参数m,以及询问数q
询问有两种,一种是将A的一段区间加上d,另一种是询问A序列的一个子区间,以这段区间作为序列B进行上面的博弈,问最优策略下先手还是后手会赢。

【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_数据结构_02

Solution

思考这个博弈的性质。

因为是两个人轮流操作,每次-1,那么可以往奇偶性的方向来思考。

考虑一个B[i]为偶数,玩家b从前面将棋子移到了这里,现在B[i]为奇数,玩家a操作。

对于每个位置i,我们可以给它定一个0/1状态,表示棋子从前面第一次移到i这里,减1以后开始操作的玩家必败还是必胜,记为F[i]。

考虑【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_线段树_03以后都求出来了,我们想要知道【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_博弈_04

如果【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_#include_05为偶数,即棋子第一次移过来时B[i]变成奇数,考虑此时先手(令他为玩家a)的选择。

  • 如果【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_博弈_06中至少有一个为0(即先手必败态),此时玩家a肯定走过去将操作权交给玩家b,玩家b必败,玩家a必胜。
  • 否则[i+1,i+m]全是先手必胜态,玩家a会原地不动,将B[i]-1,由于第一次-1后【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_线段树_07是奇数,最后必须向后走的玩家一定是玩家b,将先手必胜态留给玩家a,此时玩家a必胜。

如果【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_#include_05为奇数,即棋子第一次移过来时B[i]变成偶数。此时玩家a操作

  • 如果【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_博弈_06中至少有一个为0(即先手必败态),此时玩家a肯定走过去将操作权交给玩家b,玩家b必败,玩家a必胜。
  • 否则[i+1,i+m]全是先手必胜态,最后必须向后走的玩家一定是玩家a,将先手必胜态留给玩家b,此时玩家a必败。

综上,若B[i]为偶数,则【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_#define_10
若B[i]为奇数,则如果【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_#define_11中存在【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_#define_12,那么【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_#define_10,否则【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_线段树_14

因此【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_博弈_04只与【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_#include_05的奇偶性和【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_博弈_17有关。

回到原问题,如何处理区间询问呢?
我们可以用二进制状态将【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_博弈_17压起来,有奇偶两种转移。

用线段树维护每个区间的总转移,合并两个区间就直接合并转移即可。
这样查询就解决了。

考虑如何区间加法
区间加偶数显然没用,区间加奇数相当于奇偶调换,我们不好维护调换后的转移怎么办。
可以直接将调换后的转移存起来,因为调换两遍相当于没调换,对于每个线段树区间维护【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_数据结构_19表示区间加上0/1后的转移,区间加奇数直接将这两个swap一下就好了。

总的复杂度【杂题】[CodeForces 1076G] Array Game【数据结构】【博弈】_博弈_20

Code

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 400005
#define LL long long
using namespace std;
int lz[N],f[N][2][32],n1,t[N][2],a[N],n,m,q,d[N];
void up(int k)
{
int l=1<<m;
fo(p,0,1) fo(j,0,l-1) f[k][p][j]=f[t[k][0]][p][f[t[k][1]][p][j]];
}
void upd(int k)
{
lz[k]^=1,swap(f[k][0],f[k][1]);
}
void down(int k)
{
if(lz[k]) upd(t[k][0]),upd(t[k][1]),lz[k]=0;
}
void add(int k,int l,int r,int x,int y)
{
if(x>y||x>r||y<l) return;
if(x<=l&&r<=y) upd(k);
else
{
int mid=(l+r)>>1;
down(k);
add(t[k][0],l,mid,x,y),add(t[k][1],mid+1,r,x,y);
up(k);
}
}
void query(int k,int l,int r,int x,int y)
{
if(x>y||x>r||y<l) return;
if(x<=l&&r<=y) d[++d[0]]=k;
else
{
int mid=(l+r)>>1;
down(k);
query(t[k][0],l,mid,x,y),query(t[k][1],mid+1,r,x,y);
}
}
void build(int k,int l,int r)
{
if(l==r)
{
int le=(1<<m);
fo(j,0,le-1)
{
f[k][a[l]][j]=(j<<1)%le+1;
f[k][a[l]^1][j]=(j<le-1)?((j<<1)%le+1):(j<<1)%le;
}
return;
}
int mid=(l+r)>>1;
build(t[k][0]=++n1,l,mid);
build(t[k][1]=++n1,mid+1,r);
up(k);
}
int main()
{
cin>>n>>m>>q;
fo(i,1,n)
{
LL x;
scanf("%lld",&x);
a[i]=x%2;
}
n1=1;
build(1,1,n);
fo(i,1,q)
{
int p,x,y;
LL z;
scanf("%d%d%d",&p,&x,&y);
if(p==1)
{
scanf("%lld",&z);
if(z%2) add(1,1,n,x,y);
}
else
{
d[0]=0;
query(1,1,n,x,y);
int s=(1<<m)-1;
fod(i,d[0],1) s=f[d[i]][0][s];
if(s%2==0) printf("2\n");
else printf("1\n");
}
}
}