[bzoj4750]密码安全

来源:互联网 发布:centos yum mysql 编辑:程序博客网 时间:2024/06/01 10:50

题目大意

区间价值定义为最大值乘异或和。
求所有区间价值和。

随便搞搞

拆开来每一位单独搞,那每个位置是0或1,贡献需要有奇数个1。
首先枚举最大值,搞出它的掌控区间。
然后通过一些预处理简单得到一个区间前/后缀子区间有多少个有奇/偶数个1,就可以统计了。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=100000+10,mo=1000000061,maxws=31;int a[maxn],b[maxn],L[maxn],R[maxn],sum[maxn],num[maxn][2],cnt[maxn][2];int i,j,k,l,t,n,m,ans,ca;int read(){    int x=0,f=1;    char ch=getchar();    while (ch<'0'||ch>'9'){        if (ch=='-') f=-1;        ch=getchar();    }    while (ch>='0'&&ch<='9'){        x=x*10+ch-'0';        ch=getchar();    }    return x*f;}int getl(int l,int r,int x){    if (l>r) return 0;    int t=((sum[r]-sum[l-1])%2+2)%2;    return cnt[l][x]-cnt[r+1][(x+t)%2];}int getr(int l,int r,int x){    if (l>r) return 0;    int t=((sum[r]-sum[l-1])%2+2)%2;    return num[r][x]-num[l-1][(x+t)%2];}int main(){    ca=read();    while (ca--){        n=read();        fo(i,1,n) a[i]=read();        fo(i,1,n){            j=i-1;            while (j&&a[j]<=a[i]) j=L[j]-1;            L[i]=j+1;        }        fd(i,n,1){            j=i+1;            while (j<=n&&a[j]<a[i]) j=R[j]+1;            R[i]=j-1;        }        ans=0;        fo(j,0,maxws){            fo(i,1,n)                if (((1<<j)&a[i])!=0) b[i]=1;else b[i]=0;            fo(i,1,n)                 fo(k,0,1) num[i][k]=num[i-1][(k+b[i])%2]+(b[i]==k);            cnt[n+1][0]=cnt[n+1][1]=0;            fd(i,n,1)                 fo(k,0,1) cnt[i][k]=cnt[i+1][(k+b[i])%2]+(b[i]==k);            fo(i,1,n) sum[i]=(sum[i-1]+b[i])%2;            fo(i,1,n){                ans=(ans+(ll)a[i]*getr(L[i],i,1)%mo*getl(i+1,R[i],0)%mo*(1<<j)%mo)%mo;                ans=(ans+(ll)a[i]*getr(L[i],i,0)%mo*getl(i+1,R[i],1)%mo*(1<<j)%mo)%mo;                ans=(ans+(ll)a[i]*getr(L[i],i,1)%mo*(1<<j)%mo);            }        }        (ans+=mo)%=mo;        printf("%d\n",ans);    }}
0 0