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();
}
posted @ 2019-01-13 21:30  fcwww  阅读(137)  评论(0编辑  收藏  举报