Nim Game(2021 JLCPC I)
阿新 • • 發佈:2022-04-06
題目大意
給你\(N\)堆石子,第\(i\)堆中石子的個數是\(A_i\),現有\(M\)次操作,每次有兩種操作:\(1、\)\(1\ l\ r\ x\)表示從第\(l\)堆到第\(r\)堆,每堆石子的個數都加上\(x\);\(2、\)\(2\ l\ r\)表示問你從第\(i\)堆到第\(r\)堆,能否從中挑出若干堆使得他們的異或和為\(0\)。\((1\leq N\leq10^5,1\leq M\leq10^5,1\leq A_i\leq10^9,1\leq x\leq10^4)\)
思路
首先肯定會想到建一棵能夠支援區間修改並且查詢異或和的線段樹,但是顯然是做不了的,但是我們又能發現,當堆數超過\(32\)
的時候,一定能找到幾堆使得他們的異或和為\(0\),於是我們就可以建一棵能夠支援區間修改並單點查詢區間和的線段樹就可以了,當堆數小於等於\(32\)的時候,我們可以單點查詢並且用線性基來做,大於\(32\)了的話,就直接輸出Yes
好了,然後就能輕鬆\(AC\)了。
程式碼
#include<bits/stdc++.h> using namespace std; int a[100005]; int tree[100005<<2],laz[100005<<2]; void build(int p,int l,int r) { laz[p]=0; if(l==r) { tree[p]=a[l]; return; } int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); tree[p]=tree[p<<1]+tree[p<<1|1]; } void pushdown(int p,int l,int r) { int mid=(l+r)>>1; tree[p<<1]+=laz[p]*(mid-l+1); tree[p<<1|1]+=laz[p]*(r-mid); laz[p<<1]+=laz[p]; laz[p<<1|1]+=laz[p]; laz[p]=0; } void update(int p,int l,int r,int w,int x,int y) { if(x<=l&&r<=y) { tree[p]+=w*(r-l+1); laz[p]+=w; return; } pushdown(p,l,r); int mid=(l+r)>>1; if(x<=mid)update(p<<1,l,mid,w,x,y); if(mid<y)update(p<<1|1,mid+1,r,w,x,y); tree[p]=tree[p<<1]+tree[p<<1|1]; } int query(int p,int l,int r,int x) { if(l==r)return tree[p]; pushdown(p,l,r); int mid=(l+r)>>1; if(x<=mid)return query(p<<1,l,mid,x); else return query(p<<1|1,mid+1,r,x); } int p[35]; bool insert(int x) { for(int i=31;i>=0;i--) { if(x>>i) { if (p[i]) x ^= p[i]; else { p[i]=x; return 1; } } } return 0; } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); build(1,1,n); while(m--) { int op,l,r,x; scanf("%d%d%d",&op,&l,&r); if(op==1) { scanf("%d",&x); update(1,1,n,x,l,r); } else { if(r-l+1>32)printf("Yes\n"); else { for(int i=0;i<=31;i++)p[i]=0; bool can=0; for(int i=l;i<=r;i++) { int num=query(1,1,n,i); if(insert(num)==0) { can=1; break; } } if(can)printf("Yes\n"); else printf("No\n"); } } } return 0; }