稍加分(da)析(biao)可知,当p为奇数时,SG(x)=x&1,当p为偶数时,SG(x)=(x%(p+1)==p)?2:((x%(p+1))&1)。
奇数的话相当于区间翻转,区间异或和,直接线段树即可。
偶数的话分块来处理,每个块维护一个add标记和两个有序数组,分别表示奇余数和偶余数,区间加的时候整块的打标记,零散的直接重构,时间复杂度O(n/s+slogs)。查询的时候整块lower_bound找有多少个1和2,1的话应该是奇数数组中还没+add不超过p的,偶数数组中+p超过了add的,2的话根据add的奇偶性来决定在哪个数组查询,零散的也直接重构,时间复杂度O(nlogs/s+slogs)。
所以我也不知道s该取多少,大概250最优吧。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod (p+1)
using namespace std;
const int maxn=100100;
const int size=220;
int n,p,q,a[maxn];
inline int ID(int x) {return (x-1)/size+1;}
inline int L(int x) {return (x-1)*size+1;}
inline int R(int x) {return min(n,x*size);}
struct tree
{
tree *ls,*rs;
int l,r,sum,rev;
tree()
{
ls=rs=NULL;
sum=rev=0;
}
void pushdown()
{
if(rev)
{
if((r-l+1)&1)sum^=1;
rev=0;
if(ls!=NULL)ls->rev^=1;
if(rs!=NULL)rs->rev^=1;
}
}
void update()
{
ls->pushdown();
rs->pushdown();
sum=ls->sum^rs->sum;
}
void build(int lx,int rx)
{
l=lx;r=rx;
if(l==r) {sum=(a[l]&1);return ;}
int mid=(l+r)>>1;
(ls=new tree)->build(lx,mid);
(rs=new tree)->build(mid+1,rx);
update();
}
void reverse(int lx,int rx)
{
if(l==lx&&r==rx) {rev^=1;return ;}
pushdown();
int mid=(l+r)>>1;
if(rx<=mid) ls->reverse(lx,rx);
else if(lx>mid) rs->reverse(lx,rx);
else {ls->reverse(lx,mid); rs->reverse(mid+1,rx);}
update();
}
int query(int lx,int rx)
{
pushdown();
if(l==lx&&r==rx){return sum;};
int mid=(l+r)>>1;
if(rx<=mid) return ls->query(lx,rx);
else if(lx>mid) return rs->query(lx,rx);
else {return ls->query(lx,mid)^rs->query(mid+1,rx);}
}
}*xtr;
struct block
{
int add,rst[2][400],num[2],id;
void init(int x)
{
memset(rst,0x3f3f3f3f,sizeof(rst));
add=num[0]=num[1]=0;
id=x;
}
void build()
{
int temp=add;
init(id);
for(int i=L(id);i<=R(id);i++)
{
a[i]=(a[i]+temp)%mod;
if(a[i]&1) rst[1][++num[1]]=a[i];
else rst[0][++num[0]]=a[i];
}
sort(rst[1]+1,rst[1]+num[1]+1);
sort(rst[0]+1,rst[0]+num[0]+1);
}
void modify(int x)
{
add=(add+x)%mod;
}
void bmodify(int l,int r,int x)
{
for(int i=l;i<=r;i++)
a[i]=(a[i]+x)%mod;
build();
}
int query()
{
bool pd=(add&1);
int re,lb,ub,lb0,lb1;
lb0=lower_bound(rst[pd]+1,rst[pd]+num[pd]+2,p+1-add)-rst[pd];
lb1=lower_bound(rst[!pd]+1,rst[!pd]+num[!pd]+2,p+1-add)-rst[!pd];
re=((num[pd]-lb0+lb1)&1);
//cout<<"re1:"<<re<<endl;
lb=lower_bound(rst[pd]+1,rst[pd]+num[pd]+2,p-add)-rst[pd];
ub=lb0;
re^=(((ub-lb)&1)<<1);
//cout<<"re2:"<<re<<endl;
return re;
}
int bquery(int l,int r)
{
int re=0;
build();
for(int i=l;i<=r;i++)
{
if(a[i]==p) re^=2;
if(a[i]&1) re^=1;
}
return re;
}
}blk[600];
int main()
{
freopen("right.in","r",stdin);
freopen("right.out","w",stdout);
scanf("%d%d%d",&n,&q,&p);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]%=(p+1);
}
if(p&1) (xtr=new tree)->build(1,n);
else
for(int i=1;i<=ID(n);i++)
{
blk[i].init(i);
blk[i].build();
}
while(q--)
{
int opt,l,r,x=0;
scanf("%d%d%d",&opt,&l,&r);
if(opt==0)
{
scanf("%d",&x);
x%=mod;
if(p&1) {if(x&1) xtr->reverse(l,r);}
else
{
if(ID(l)==ID(r)) blk[ID(l)].bmodify(l,r,x);
else
{
blk[ID(l)].bmodify(l,R(ID(l)),x);
blk[ID(r)].bmodify(L(ID(r)),r,x);
for(int i=ID(l)+1;i<=ID(r)-1;i++)
blk[i].modify(x);
}
}
}
else
{
if(p&1) printf("%d\n",(xtr->query(l,r)!=0));
else
{
int ans=0;
if(ID(l)==ID(r)) ans^=blk[ID(l)].bquery(l,r);
else
{
ans^=blk[ID(l)].bquery(l,R(ID(l)));
ans^=blk[ID(r)].bquery(L(ID(r)),r);
for(int i=ID(l)+1;i<=ID(r)-1;i++)
ans^=blk[i].query();
}
printf("%d\n",(ans!=0));
}
}
}
}