Description
有些人在社交网络中使用过许多的密码,我们通过将各种形式的信息转化为 01 信号,再转化为整数,可以将这个人在一段时间内使用过的密码视为一个长度为 n 的非负整数序列 A_1,A_2,…,A_n 。一个人相邻几次在社交网络中使用的密码很有可能是类似的,这使得密码并不是足够安全。为了检验某些人在某些时间段内是否可能受到不安全的影响,我们需要计算上述序列的复杂程度。
的值,这将作为我们评估密码复杂程度的一个部分。由于答案可能很大,你只需要给出答案对10^9+61 取模的值即可。
题解:
本来听了scy的话,想去学一下单调栈,找到这题,发现最难学的不是单调栈,而是这个xor和的处理。首先看到这种题,思路一般是统计出每个数作为最大值对答案产生的贡献,所以我们用单调栈预处理出每个数左边第一个比他大的和右边第一个比他大的位置,维护一个栈底到栈顶单调递减的单调栈就好了。然后就是区间xor和的问题,我们都知道,若
sum[i]
表示
a[1]到a[i]
的异或和,那么
a[l] xor a[l+1]....xor a[r]=sum[l−1] xor sum[r]
,然后对于一个位置
i
,若
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod=1000000061;
const int Maxn=100010;
int n,sta[Maxn],rs[Maxn],ls[Maxn];
LL a[Maxn],sum[Maxn],s[2][32][Maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
sum[0]=0;
for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]^a[i];
int top=0;
for(int i=1;i<=n;i++)
{
while(top&&a[sta[top]]<a[i])rs[sta[top--]]=i-1;
ls[i]=sta[top]+1;sta[++top]=i;
}
while(top)rs[sta[top--]]=n;
for(int i=0;i<=n;i++)
for(int j=0;j<30;j++)
s[0][j][i]=((!i)?0:s[0][j][i-1]),s[1][j][i]=((!i)?0:s[1][j][i-1]),s[(sum[i]>>j)&1][j][i]++;
LL ans=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<30;j++)
{
LL t1=(s[0][j][i-1]-((ls[i]==1)?0:s[0][j][ls[i]-2]))*(s[1][j][rs[i]]-s[1][j][i-1]);
//左边0右边1产生的1
LL t2=(s[1][j][i-1]-((ls[i]==1)?0:s[1][j][ls[i]-2]))*(s[0][j][rs[i]]-s[0][j][i-1]);
//左边1右边0产生的1
ans=(ans+((t1+t2)<<j)%mod*a[i]%mod)%mod;
}
}
printf("%lld\n",ans);
}
}