BZOJ4750: 密码安全

BZOJ4750: 密码安全

https://lydsy.com/JudgeOnline/problem.php?id=4750

分析:

  • 对区间进行分治,每次取出最大值,然后枚举二进制位预处理二进制每一位前缀和更新答案。
  • 建笛卡尔树。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define mod 1000000061
#define N 100050
#define ls ch[p][0]
#define rs ch[p][1]
typedef long long ll;
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {
    int x=0; char s=nc();
    while(s<'0') s=nc();
    while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    return x;
}
ll ans;
int n,a[N],ch[N][2],s[N][35][2],S[N],tp,fa[N],vis[N];
ll ss(int l,int r,int o,int p) {
    if(l==0) return s[r][p][o];
    else return s[r][p][o]-s[l-1][p][o];
}
void dfs(int l,int r,int p) {
    if(!p) return ;
    dfs(l,p-1,ls);
    dfs(p+1,r,rs);
    ll g=0;
    int i;
    for(i=0;i<31;i++) {
        g=(g+(ss(l-1,p-1,0,i)*ss(p,r,1,i)+ss(l-1,p-1,1,i)*ss(p,r,0,i))%mod*(1<<i))%mod;
    }
    ans=(ans+a[p]*g)%mod;
}
void solve() {
    n=rd();
    int i,x=0,j;
    tp=0;
    memset(s[0],0,sizeof(s[0]));
    for(i=0;i<31;i++) s[0][i][0]=1;
    for(i=1;i<=n;i++) {
        vis[i]=fa[i]=ch[i][0]=ch[i][1]=0;
        a[i]=rd();
        x^=a[i];
        for(j=30;j>=0;j--) {
            int k=(x>>j)&1;
            s[i][j][k]=s[i-1][j][k]+1;
            s[i][j][!k]=s[i-1][j][!k];
        }
        while(tp&&a[i]>=a[S[tp]]) {
            if(tp==1||a[i]<a[S[tp-1]]) {
                ch[i][0]=S[tp]; tp--;
                break;
            } ch[S[tp-1]][1]=S[tp]; tp--;
        }
        S[++tp]=i;
    }
    while(tp>1) ch[S[tp-1]][1]=S[tp],tp--;
    int rt=0;
    for(i=1;i<=n;i++) vis[ch[i][0]]=vis[ch[i][1]]=1;
    for(i=1;i<=n;i++) if(!vis[i]) {rt=i; break;}
    ans=0; dfs(1,n,rt);
    printf("%lld\n",ans);
}
int main() {
    int T;scanf("%d",&T);
    while(T--) solve();
}

转载于:https://www.cnblogs.com/suika/p/10264114.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值