【题目】
【分析】
考虑一个区间,只要这个区间的任意非最大值有一位不与最大值相同,那么这个区间就是合法区间。
因为如果有一位不同,那么或起来肯定值会变大,那么就是合法的。
找出所有值左右第一个大于它的位置,那么以它为最大值的区间就固定在这一段中。只要再找出这个区间中左右第一个有一位不与最大值相同的值的位置,那么这个位置左边的所有位置都可以与最大值右边的位置构成一个合法区间。右边也同理。
可以用单调栈找出左右第一个大于它的位置,再用 O 的时间处理出左右第一个有某一位不与它相同的数的位置,然后就可以 O 统计答案了,注意处理值相同的情况。
时间复杂度 O 。
【代码】
#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;
int n,a[N];
stack<int>sta;
int L[N],R[N],Dl[N],Dr[N],Max[N],Min[N];
int main()
{
// freopen("string.in","r",stdin);
// freopen("string.out","w",stdout);
int i,j;
scanf("%d",&n);
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
for(i=1;i<=n;++i)
{
while(!sta.empty()&&a[i]>=a[sta.top()]) sta.pop();
L[i]=(sta.empty()?1:sta.top()+1);sta.push(i);
}
while(!sta.empty()) sta.pop();
for(i=n;i>=1;--i)
{
while(!sta.empty()&&a[i]>a[sta.top()]) sta.pop();
R[i]=(sta.empty()?n:sta.top()-1);sta.push(i);
}
for(i=1;i<=n;++i)
{
Dl[i]=0;
for(j=0;(1<<j)<=a[i];++j)
{
if((1<<j)&a[i]) Max[j]=max(Max[j],i);
else Dl[i]=max(Dl[i],Max[j]);
}
}
fill(Min,Min+40,n+1);
for(i=n;i>=1;--i)
{
Dr[i]=n+1;
for(j=0;(1<<j)<=a[i];++j)
{
if((1<<j)&a[i]) Min[j]=min(Min[j],i);
else Dr[i]=min(Dr[i],Min[j]);
}
}
long long ans=0;
for(i=1;i<=n;++i)
{
if(Dl[i]>=L[i]) ans+=1ll*(Dl[i]-L[i]+1)*(R[i]-i+1);
if(Dr[i]<=R[i]) ans+=1ll*(R[i]-Dr[i]+1)*(i-L[i]+1);
if(Dl[i]>=L[i]&&Dr[i]<=R[i]) ans-=1ll*(Dl[i]-L[i]+1)*(R[i]-Dr[i]+1);
}
printf("%lld",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}