经典问题,真难则反后算贡献
我们发现算>难度很大,不妨考虑算=后再用总区间减去非法区间
对于计算区间,可以考虑对于每个点维护以他为最大值的范围。
这可以二分求取,之后在算出来的左右区间内继续二分求或值等于他本身的
然后根据乘法原理计算贡献。注意的是,为了避免重复计算并且为了防止漏算。
我们要特殊关注一种情况,就是当两个值相等的时候,我们计算以其中一个为最大值的时候,我们对于check函数,右边是<=,而左边是<
这样可以不重不漏的枚举答案
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=2e5+10; const int inf=1e9; ll a[N]; ll st[N][25]; ll tmp[N][25]; ll n; int lg[N]; void init(int x){ st[x][0]=a[x]; tmp[x][0]=a[x]; int i; for(i=1;i<=20;i++){ st[x][i]=max(st[x][i-1],st[min(1ll*x+(1<<(i-1)),n)][i-1]); tmp[x][i]=tmp[x][i-1]|tmp[min(1ll*x+(1<<(i-1)),n)][i-1]; } } int query(int x,int y){ int len=lg[y-x+1]; return (tmp[x][len]|tmp[y-(1<<len)+1][len]); } int check(int l,int r){ int len=lg[r-l+1]; return max(st[l][len],st[r-(1<<len)+1][len]); } ll get(int x){ int l=1,r=x-1; int ans1=x; int ans2=x; while(l<r){ int mid=l+r>>1; if(check(mid,x-1)<a[x]){ r=mid; ans1=mid; } else l=mid+1; } if(check(l,x-1)<a[x]&&x!=1) ans1=l; l=ans1,r=x; while(l<r){ int mid=l+r>>1; if(query(mid,x)==a[x]){ r=mid; } else{ l=mid+1; ans1=mid+1; } } int l2=x+1,r2=n; while(l2<r2){ int mid=l2+r2+1>>1; if(check(x+1,mid)<=a[x]){ l2=mid; ans2=mid; } else{ r2=mid-1; } } if(check(x+1,l2)<=a[x]&&x!=n) ans2=l2; l2=x,r2=ans2; while(l2<r2){ int mid=l2+r2+1>>1; if(query(x,mid)==a[x]){ l2=mid; } else{ r2=mid-1; ans2=mid-1; } } return (1ll*ans2-x+1)*(1ll*x-ans1+1); } int main(){ ios::sync_with_stdio(false); int i; cin>>n; lg[1]=0; lg[2]=1; for(i=3;i<N;i++){ lg[i]=lg[i>>1]+1; } for(i=1;i<=n;i++){ cin>>a[i]; } for(i=n;i>=1;i--){ init(i); } ll ans=n*(n+1)/2; for(i=1;i<=n;i++){ ans-=get(i); } cout<<ans<<endl; return 0; }View Code