CF875D High Cry(二分+分治)

经典问题,真难则反后算贡献

我们发现算>难度很大,不妨考虑算=后再用总区间减去非法区间

对于计算区间,可以考虑对于每个点维护以他为最大值的范围。

这可以二分求取,之后在算出来的左右区间内继续二分求或值等于他本身的

然后根据乘法原理计算贡献。注意的是,为了避免重复计算并且为了防止漏算。

我们要特殊关注一种情况,就是当两个值相等的时候,我们计算以其中一个为最大值的时候,我们对于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
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/13748207.html