Codeforces Round #512 (Div. 1) B. Vasya and Good Sequences
阿新 • • 發佈:2018-12-11
粘不過來題目,自己點開看吧QAQ
題目大意:給你一列數,現在有一種操作,就是對一個數,可以任意交換其二進位制的兩位,操作次數不限。問你有多少個字串 [ l , r ],可以經過上述操作,使該字串的異或和為0。
題解:
一段數的異或和為0,即讓這一串數二進位制下每一位的1的個數都為0。於是題目可以簡化成:先搞出來每個數的二進位制下1的個數,比如:3 4 6 7,然後每次可以挑出兩個數,讓它們都減1(即消去一位)。然後看能不能把所有數都變成0。
進一步觀察,我們發現只要最大的數小於等於剩餘的數之和即可消完。即對於一段數 max(l,r) * 2 <= sum(l,r) 且 sum(l,r)為偶數即可。
對於這個問題,我們發現任意的數的二進位制下1的個數都大於0,且小於等於64。也就是 max(l,r) 小於等於64,max(l,r)*2 <=128 ,那麼對於長度大於128的區間,一定滿足 max(l,r) * 2 <= sum(l,r) ,我們只需要找有多少個區間滿足 sum(l,r)為偶數。
對於長度小於等於128的區間,暴力判斷即可。
RMQ打錯真是沒誰了QAQ
程式碼:
#include<bits/stdc++.h> #define LL long long #define MAXN 300050 using namespace std; int n,b[MAXN],cf[25],Log2[MAXN],MX[MAXN][25],s[MAXN],js[MAXN],os[MAXN]; LL a[MAXN]; void RMQ() { int i,j; for(i=1;i<=n;i++)MX[i][0]=b[i]; for(j=1;cf[j]<=n;j++) { for(i=1;i+cf[j]-1<=n;i++) { MX[i][j]=max(MX[i][j-1],MX[i+cf[j-1]][j-1]); } } /*for(i=1;i<=n;i++) { for(j=1;i+cf[j]-1<=n;j++) { MX[i][j]=max(MX[i][j-1],MX[i+cf[j-1]][j-1]); } }*/ } int Mx(int ql,int qr) { int i=Log2[qr-ql+1]; return max(MX[ql][i],MX[qr-cf[i]+1][i]); } int main() { int l1,i,len,l,r,mx1; LL aa,ans; scanf("%d",&n); cf[0]=1;for(i=1;i<=20;i++)cf[i]=cf[i-1]*2; for(i=1;i<=300000;i++)Log2[i]=log2(i); for(i=1;i<=n;i++)scanf("%lld",&a[i]); for(i=1;i<=n;i++) { aa=a[i]; while(aa>0LL) { b[i]+=(int)(aa%2LL); aa/=2LL; } } s[0]=0;for(i=1;i<=n;i++)s[i]=s[i-1]+b[i]; RMQ(); //長度小於等於128,暴力判斷 ans=0LL; for(len=1;len<=128;len++) { for(l=1;l+len-1<=n;l++) { r=l+len-1; mx1=Mx(l,r); if((s[r]-s[l-1])%2==0&&mx1*2<=s[r]-s[l-1])ans++; } } //長度大於128,sum一定大於等於max*2,只需要判斷sum是否是偶數 memset(js,0,sizeof(js)); memset(os,0,sizeof(os)); for(i=n;i>=0;i--) { js[i]+=js[i+1];os[i]+=os[i+1]; if(s[i]%2==0)os[i]++; else js[i]++; } for(l=1;l<=n;l++) { l1=l-1;r=l+128-1; if(r+1<=n) { if(s[l1]%2==0) { ans+=(LL)os[r+1]; } else { ans+=(LL)js[r+1]; } } } printf("%lld",ans); return 0; }