传送门
解析:
首先这道题卡空间,虽然说O(nlogn)O(nlogn)O(nlogn)的时间复杂度可过,但是要是空间复杂度不是O(n)O(n)O(n)可能就会咕咕咕。。。
思路:
首先,发现一个性质,就是满足条件的区间中必然有某个数在某个二进制位上有最大值没有的1。
那么我们可以预处理所有数在前缀(后缀)或的结果不变的情况下,向左和向右最远能够延伸的地方。
然后我们处理出每个数作为最大值向左向右能够延伸的最远地方。这个用单调栈来实现就行了。
然后就是统计了,我们考虑以最左最大值为标志统计这个区间,所以可以做到不重不漏,同时单调栈定左右端点的时候就会有所区别。
剩下的就是容斥原理了,每个点看它能够管辖的范围中能够有多少能够成为它合法的左右端点,再减去两端都合法算重的情况就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
cs int N=1000006;
int bin[35];
int s[N],st[N],top;
int pre[N],suf[N];
int lbound[N],rbound[N];
int n;
ll ans;
signed main(){
n=getint();
for(int re i=1;i<=n;++i)s[i]=getint();
for(int re i=1;i<=n;++i){
while(top&&s[st[top]]<=s[i])--top;
lbound[i]=st[top];
st[++top]=i;
for(int re j=0;(1<<j)<=s[i];++j){
if((1<<j)&s[i])bin[j]=i;
else pre[i]=max(pre[i],bin[j]);
}
}
fill(bin,bin+35,n+1);
st[top=0]=n+1;
for(int re i=n;i;--i){
while(top&&s[st[top]]<s[i])--top;
rbound[i]=st[top];
st[++top]=i;
suf[i]=n+1;
for(int re j=0;(1<<j)<=s[i];++j){
if((1<<j)&s[i])bin[j]=i;
else suf[i]=min(suf[i],bin[j]);
}
}
for(int re i=1;i<=n;++i){
if(pre[i]>lbound[i])ans+=(ll)(pre[i]-lbound[i])*(rbound[i]-i);
if(suf[i]<rbound[i])ans+=(ll)(i-lbound[i])*(rbound[i]-suf[i]);
if(pre[i]>lbound[i]&&suf[i]<rbound[i])ans-=(ll)(pre[i]-lbound[i])*(rbound[i]-suf[i]);
}
cout<<ans;
return 0;
}