成绩调研

来源:互联网 发布:linux 查看局域网ip 编辑:程序博客网 时间:2024/04/30 04:14

题目大意

求有多少个区间满足权值为i的数量在[li,ri]内。

扫一扫

我们枚举区间右端点,左端点的可取范围是一段区间。
尝试用两个堆维护左端点的最大值和最小值。
对于第i种权值,我们可以通过指针跳跳跳获得在位置i时左端点的左界和右界,所以一个堆保存所有权值的左界另一个保存右界。
注意l=0以及r=0的情况。

#include<cstdio>#include<algorithm>#include<set>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long LL;const int maxn=200000+10;multiset<int> s1,s2;int L[maxn],R[maxn],ll[maxn],rr[maxn],fi[maxn],right[maxn],id[maxn],cnt[maxn],a[maxn];int i,j,k,l,r,t,n,m;LL ans;int read(){    int x=0;    char ch=getchar();    while (ch<'0'||ch>'9') ch=getchar();    while (ch>='0'&&ch<='9'){        x=x*10+ch-'0';        ch=getchar();    }    return x;}int main(){    freopen("survey.in","r",stdin);freopen("survey.out","w",stdout);    n=read();m=read();    fo(i,1,n) a[i]=read();    fo(i,1,m) L[i]=read(),R[i]=read();    fo(i,1,m) cnt[i]=1;    fd(i,n,1){        right[i]=fi[a[i]];        fi[a[i]]=i;    }    fo(i,1,n) id[i]=++cnt[a[i]];    id[0]=1;    fo(i,1,m){        ll[i]=1;        s1.insert(-ll[i]);        if (L[i]){            rr[i]=0;            s2.insert(rr[i]);        }    }    fo(i,1,n){        t=a[i];        if (L[t]){            while (id[i]-id[rr[t]]+1>L[t]){                s2.erase(s2.find(rr[t]));                if (!rr[t]) rr[t]=fi[t];else rr[t]=right[rr[t]];                s2.insert(rr[t]);            }        }        while (id[i]-id[ll[t]-1]+1>R[t]+1){            s1.erase(s1.find(-ll[t]));            if (ll[t]==1) ll[t]=fi[t]+1;else ll[t]=right[ll[t]-1]+1;            s1.insert(-ll[t]);        }        l=-(*s1.begin());        if (!s2.empty()) r=*s2.begin();else r=i;        if (l<=r) ans+=(LL)(r-l+1);    }    printf("%lld\n",ans);}
0 0
原创粉丝点击