[bzoj4750]密码安全

题目大意

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

随便搞搞

拆开来每一位单独搞,那每个位置是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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值