題目連接:https://nanti.jisuanke.com/t/40258node
題意:給長爲n的數組a,有m次操做,包括單點修改和查詢F(l,r),其值爲全部f(i,j)的異或和,l<=i<=j<=r,即數組
其中ui
(n,m<=1e5).spa
思路:這種題能夠用線段樹來進行修改和查詢,但須要先化簡。對於l<=x<=r,包括ax的區間有(r-x+1)*(x-l+1)個,注意到當區間長爲偶數時,改值恆爲偶數,那麼也就是說ax出現偶數次,那麼查詢結果爲0。當區間長度爲奇數時,若x與l奇偶性不一樣,則該值爲偶數,異或值爲0; 若奇偶值相同,則該值爲奇數,異或值爲ax,故須要用線段樹維護與l奇偶性相同的元素的異或和。code
可是合併兩個區間的時候,可能出現右區間的l與左區間的l奇偶性不一樣的狀況,這時,右區間維護的值不能直接求與。須要同右區間總體異或和異或以後獲得與左區間的l奇偶性相同的那些元素的異或和。說的很繞,手動算一下就明白,因此咱們還須要用線段樹維護區間的異或和。blog
AC代碼:it
#include<cstdio> #include<algorithm> using namespace std; const int maxn=100005; int T,n,m,cas,a[maxn]; struct node{ int l,r; int sum,val; }tr[maxn<<2]; void pushup(int v){ tr[v].sum=tr[v<<1].sum^tr[v<<1|1].sum; if((tr[v<<1|1].l-tr[v].l)%2==0) tr[v].val=tr[v<<1].val^tr[v<<1|1].val; else tr[v].val=tr[v<<1].val^(tr[v<<1|1].sum^tr[v<<1|1].val); } void build(int v,int l,int r){ tr[v].l=l,tr[v].r=r; if(l==r){ tr[v].sum=tr[v].val=a[l]; return; } int mid=(l+r)>>1; build(v<<1,l,mid); build(v<<1|1,mid+1,r); pushup(v); } void update(int v,int x,int y){ if(tr[v].l==tr[v].r){ tr[v].sum=tr[v].val=y; return; } int mid=(tr[v].l+tr[v].r)>>1; if(x<=mid) update(v<<1,x,y); else update(v<<1|1,x,y); pushup(v); } void query(int v,int l,int r,int& x,int& y){ if(tr[v].l==l&&tr[v].r==r){ x=tr[v].val; y=tr[v].sum; return; } int mid=(tr[v].l+tr[v].r)>>1; if(r<=mid) query(v<<1,l,r,x,y); else if(l>mid) query(v<<1|1,l,r,x,y); else{ int a,b,c,d; query(v<<1,l,mid,a,b); query(v<<1|1,mid+1,r,c,d); if((tr[v<<1|1].l-l)%2==0) x=a^c,y=b^d; else x=a^(d^c),y=b^d; } } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); build(1,1,n); printf("Case #%d:\n",++cas); while(m--){ int op,x,y; scanf("%d%d%d",&op,&x,&y); if(op==0) update(1,x,y); else{ if((y-x+1)%2==0) printf("0\n"); else{ int a,b; query(1,x,y,a,b); printf("%d\n",a); } } } } return 0; }