2019牛客暑期多校训练营(第二场)J.Subarray

题意:给你一个n 表示有n段连续的1序列 现在问你 在总长度为0~1e9-1的范围内有多少个大于0的子段

思路:假设我们统计了当前的前缀和 我们显然可以用树状数组维护一下前缀和 这样我们可以nlogn算出答案 但是对于1e7的数据 这样处理肯定会超时 所以我们考虑前缀和是一个每次变化都是1的折线

我们可以直接数组模拟lazy来求出答案

#include <bits/stdc++.h>
using namespace std;
const int N = 1e7+7;
const int inf = 0x3f3f3f3f;
typedef long long ll;
const ll mod = 1e7+9;
int l[N],r[N];
int f[N],g[N],sum[3*N];
int b[3*N],c[3*N];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n; cin>>n;
    for(int i=1;i<=n;i++){
        cin>>l[i]>>r[i];
    }
    f[1]=r[1]-l[1]+1;
    for(int i=2;i<=n;i++)
        f[i]=max(r[i]-l[i]+1,f[i-1]-(l[i]-r[i-1]-1)+r[i]-l[i]+1);
    g[n]=r[n]-l[n]+1;
    for(int i=n-1;i>=1;i--)
        g[i]=max(r[i]-l[i]+1,g[i+1]-(l[i+1]-r[i]-1)+r[i]-l[i]+1);
    int i=1; int now=1e7;
    ll ans=0;
    while(i<=n){
        int j=i+1;
        while(j<=n&&g[j]+f[j-1]>=(l[j]-r[j-1]-1)){
            j++;
        }
        j--;
        int le=max(0,l[i]-g[i]); int ri=min(int(1e9)-1,r[j]+f[j]);
        int t=i; int mx,mi; mx=-1; mi=inf;
        for(int k=le;k<=ri;k++){
            if(k>=l[t]&&k<=r[t])
                sum[k-le+1]=sum[k-le]+1;
            else
                sum[k-le+1]=sum[k-le]-1;
            if(k==r[t]) t++;    
            mx=max(mx,sum[k-le+1]+now);
            mi=min(mi,sum[k-le+1]+now);
            b[sum[k-le+1]+now]++;
        }
        for(int k=mx-1;k>=mi;k--)
            b[k]+=b[k+1];
        ans+=b[now+1];
        for(int k=le;k<=ri;k++){
            t=sum[k-le+1]+now;
            b[t+1]-=c[t+1];
            c[t]+=c[t+1]+1;
            c[t+1]=0;
            ans+=b[t+1];
        }
        for(int k=mi;k<=mx;k++)
            b[k]=0,c[k]=0;
        i=j+1;
    }
    cout<<ans<<endl;
}

猜你喜欢

转载自www.cnblogs.com/wmj6/p/11288038.html