Description
lyk拥有一个区间。
它规定一个区间的价值为这个区间中所有数and起来的值与这个区间所有数or起来的值的乘积。
例如3个数2,3,6。它们and起来的值为2,or起来的值为7,这个区间对答案的贡献为2*7=14。
现在lyk有一个n个数的序列,它想知道所有n*(n+1)/2个区间的贡献的和对1000000007取模后的结果是多少。
例如当这个序列为{3,4,5}时,那么区间[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]的贡献分别为9,0,0,16,20,25。
第一行一个数n(1<=n<=100000)。
接下来一行n个数ai,表示这n个数(0<=ai<=10^9)。
Analysis
对于这些位运算的题,有一个套路:拆位计算。
我们采用这个套路,按位计算,显然该位仅有连续一段全为1的序列才会对答案有贡献。
这样他们在该位上and起来的贡献已知,只需求出or起来的贡献。
对于这个,跟其所在序列中的位置有关,可以自己乱搞一下。
时间复杂度是
O(nlog2(109))
,1000ms整碾过。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N=100010,mo=1e9+7;
ll n,b[32];
int _2[32],a[N];
char ch;
int read()
{
int t=0;
for(ch=getchar();ch<'0' || ch>'9';ch=getchar());
for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';
return t;
}
int main()
{
_2[0]=1;
fo(i,1,30) _2[i]=_2[i-1]*2;
n=read();
ll ans=0;
fo(i,1,n) a[i]=read();
fo(j,0,30)
{
ll sum=0,last=0;
memset(b,0,sizeof(b));
fo(i,1,n)
if(a[i]&_2[j])
{
fo(k,0,30)
{
if(a[i]&_2[k]) b[k]=i;
(sum+=_2[k]*(b[k]-last))%=mo;
}
}
else
{
fo(k,0,30) b[k]=i;
last=i;
}
(ans+=sum*_2[j])%=mo;
}
printf("%lld",ans);
return 0;
}