题目链接:点击打开链接
这个题很明显是要从位运算的角度去考虑的。
考虑“与运算“ 和 ”或运算“ 的性质。
与运算,是只要有一个0,那么就都是0
或运算,是只要有一个1,那么就都是1
考虑样例:
3: 011
4: 100
5: 101
假如现在扫描到了第三个数5,那么就是要求 (3 & 4& 5) * (3|4|5)
按位扫描,最高位有2个连续的1,意味着最高位可以对答案贡献2次,
然后扫描各个位,发现在上下两个的范围内,第二位是0,第三位是1。
所以在这个过程中,只需要维护一个seq数组,seq[x]表示第x个位到现在为止有多少个连续的1.
再维护一个near数组,near[x]离当前最近的第x位上为1的数的下标。
当near在seq的上面,那么就说明这里的第x个位都不可能或出1来
当near在seq的下面,就说明有一部分可以或出1来。
#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<stdio.h>
#define LL long long int
using namespace std;
const int maxn = 110000;
int seq[32];
int near[32];
int n;
LL arr[maxn];
const int mod = 1000000007;
void init()
{
memset(seq,0,sizeof(seq));
memset(near,-1,sizeof(near));
}
int main()
{
while(cin>>n)
{
init();
for(int i = 1;i<=n;i++)
{
scanf("%lld",&arr[i]);
}
LL ans = 0;
for(int i = 1;i<=n;i++)
{
int pt = 0;
int x = arr[i];
do
{
if( (x%2) == 1 )
{
seq[pt] ++;
near[pt] = i;
}
else
{
seq[pt] = 0;
}
x/=2;
pt++;
}while(x!=0);
for(int j = pt;j<=31;j++)
{
seq[j] = 0;
}
for(int j = 0;j<=31;j++)
{
if(seq[j] == 0) continue;
else
{
int x = seq[j];
LL ft = ( 1LL<<j );
LL sc = 0;
ft %= mod;
for(int k = 0;k<=31;k++)
{
if(i - near[k] + 1> x) continue;
else
{
sc += (LL)(x - (i - near[k]) ) * (1LL<<k);
sc %= mod;
}
}
ans += ft*sc;
ans %= mod;
}
}
}
printf("%lld\n",ans);
}
return 0;
}