經典題 所有子區間和的異或和
阿新 • • 發佈:2020-11-20
兩個經典的題目
1.求所有子區間異或和的和
2.求所有子區間和的異或和
第一個我們討論過 也做出來了
主要是第二個
首先肯定要進行字首和 記得把sum[0]算上
記sum為字首和陣列
顯然 一個區間的和 為sum[i]-sum[j-1]
另外異或肯定要拆位來看
考慮第k位 設 lim[i]為sum[i]%(2^k)
顯然
當sum[i]的第k位為1時,並且sum[j-1]的第k位為0時 需要滿足 lim[i]>=lim[j-1]
當sum[i]的第k位為1時,並且sum[j-1]的第k位為1時 需要滿足 lim[i]<lim[j-1] (這樣才會向高位借位 在k位產生1)
第k位為0時同理
顯然這是一個偏序關係 用樹狀陣列維護即可
#include<bits/stdc++.h> using namespace std; const int N = 2*(1e6+100); typedef long long ll; ll c[2][N],a[100005],p[30]; void add(int x,int v,int g){ if(x==0){ c[g][x]+=v; return; } while(x<N){ c[g][x]+=v; x+=x&-x; } } ll query(int x,int g){ int ret=c[g][0]; while(x){ ret+=c[g][x]; x-=x&-x; } return ret; } int main(){ p[0]=1; for(int i = 1; i <= 25; i++) p[i]=2*p[i-1]; int n; scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%d",&a[i]),a[i]+=a[i-1]; a[0]=0; ll ans = 0; for(int k = 0; (1<<k) <= a[n]; k++){ ll ret = 0; for(int i = 0; i <= n; i++){ int g = ((a[i]>>k)&1); ll lb = a[i]%p[k]; if(g){ ret+=query(lb,0)+query(N-1,1)-query(lb,1); add(lb,1,1); }else{ ret+=query(lb,1)+query(N-1,0)-query(lb,0); add(lb,1,0); } } if(ret%2) ans+=(1<<k); memset(c,0,sizeof(c)); } printf("%lld\n",ans); return 0; }