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
",ans);
}
int main() {
int T;scanf("%d",&T);
while(T--) solve();
}