1. 程式人生 > >【NOIP校內模擬】T2 字胡串(分治)

【NOIP校內模擬】T2 字胡串(分治)

%%%%%%%%%%%lst神仙 這是他的做法 吊了標算

對於這種有多少區間滿足要求的 我們套路的用分治做 每次都統計左端點在左半邊 右端點在右半邊的個數

設f(i) 表示當前點到中間分割點的最大值,g(i)表示當前點到中間分割點的或和

我們發現 g(i)≥f(i) 所以只需找到g[i]=f[i]的區間就好

然後f肯定是單調遞增的

所以可以維護雙指標

邊界條件很壞壞 膜lst神仙啊

#include<bits/stdc++.h>
#define N 1000005
#define int long long
using namespace std;
template<class T>
inline void read(T &x)
{
    x=0; int f=1;
    static char ch=getchar();
    while((!isdigit(ch))&&ch!='-')  ch=getchar();
    if(ch=='-') f=-1;
    while(isdigit(ch))  x=x*10+ch-'0',ch=getchar();
    x*=f;
}
int n,s[N],f[N],g[N],ans;
inline void solve(int l,int r)
{
    if(l>=r)    return;
    int mid=(l+r)>>1;
    f[mid]=s[mid],f[mid+1]=s[mid+1];
    for(int i=mid-1;i>=l;i--)   f[i]=max(s[i],f[i+1]);
    for(int i=mid+2;i<=r;i++)   f[i]=max(s[i],f[i-1]);
    g[mid]=s[mid],g[mid+1]=s[mid+1];
    for(int i=mid-1;i>=l;i--)   g[i]=s[i]|g[i+1];
    for(int i=mid+2;i<=r;i++)   g[i]=s[i]|g[i-1];
    int pos=mid;
    for(int i=mid;i>=l;i--) //左端點 
    {
        if(f[i]==g[i])
        {
            while(pos<r&&((g[pos+1]|g[i])==g[i])&&f[pos+1]<=f[i])   pos++;  //1.沒越界 2.是子集 3.最大值在左邊 
            ans-=pos-mid;
        }
    } 
    pos=mid+1;
    for(int i=mid+1;i<=r;i++)
    {
        if(f[i]==g[i])
        {
            while(pos>l&&((g[pos-1]|g[i])==g[i])&&f[pos-1]<f[i])    pos--;
            ans-=mid-pos+1;
        }
    } 
    solve(l,mid);
    solve(mid+1,r);
}
main()
{
    read(n);
    for(int i=1;i<=n;i++)   read(s[i]);
    ans=n*(n-1)/2;
    solve(1,n);
    cout<<ans;
    return 0;
}