很容易发现,一个序列的前缀and/or值可分为 log(V) 块
其中V是序列中元素的最大值
然后这题就很好做了
枚举区间的右端点i,同时维护每一个块的范围和值
显然有and块和or块两种分开维护
那么i往后推一个,其实就是把每个块的值and/or上当前位置,再加上单点i
然后合并相邻且值相等的块
扫一波统计答案即可
示例程序:
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=100005,tt=1000000007;
int n,topo,topa;
struct data{
int l,w;
data () {}
data (int _l,int _w):l(_l),w(_w) {}
bool operator==(const data&b)const{
return w==b.w;
}
}o[maxn],a[maxn];
void merge(data *a,int &n){
// n=unique(a+1,a+1+n)-a-1;
int m=1;
for (int i=2;i<=n;i++)
if (a[i].w!=a[i-1].w) a[++m]=a[i];
n=m;
}
int main(){
scanf("%d",&n);
topo=topa=0;int ans=0;
for (int k=1;k<=n;k++){
int x;scanf("%d",&x);
for (int j=1;j<=topa;j++) a[j].w&=x;
for (int j=1;j<=topo;j++) o[j].w|=x;
a[++topa]=data(k,x); o[++topo]=data(k,x);
merge(o,topo);merge(a,topa);
for (int i=topa,j=topo,lst=k;i&&j;){
int t=max(a[i].l,o[j].l);
ans=(ans+(LL)a[i].w*o[j].w%tt*(lst-t+1)%tt)%tt;
i-=(t==a[i].l);j-=(t==o[j].l);lst=t-1;
}
}
printf("%d",ans);
return 0;
}