[联合集训6-25] 蓝雨 线段树+主席树+hash

23 篇文章 0 订阅
4 篇文章 0 订阅

先考虑 p=q p = q 的情况,习惯先把求第 k k 大变成求第k小。
那么我们逐个确定第 k k 小的串每种数字包含了多少个。假设当前我们已经确定了x1之前的数的个数,此时对于每个左端点 i i ,合法的右端点都是一个区间[li,ri]。现在考虑二分确定 x x 的个数,我们把序列中为x的位置单独挑出来,这些位置把序列分成若干段,假如二分有 mid m i d x x ,那么对于每一段,合法的右端点又会有一个小于之后第mid的个 x x 的位置的一个限制,然后再求合法区间个数。最后我们成功得到x的个数为 num n u m 之后,每一段的合法右端点区间就要和其往后第 num n u m 段求交。
以上这些区间求交操作就是对 li l i chkmax或者对 ri r i chkmin,又因为 li,ri l i , r i 是随着 i i 单调不降的,我们可以用一棵线段树维护这些li,ri
求出第 p p 小之后,考虑如何推到第q小。注意到左端点固定时,随着右端点增大,串的大小也是增大的,于是我们可以找出每一个左端点第一个比 p p 大的右端点并用一个堆来维护他们,每次弹出最小的[l,r]之后再把 [l,r+1] [ l , r + 1 ] 加入堆。至于比较两个串的大小,可以用一棵主席树维护hash值,在上面二分找到第一个hash值不同的地方即可。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define N 100010
#define ll long long
#define min(x,y) (x<y?x:y)
#define max(x,y) (x<y?y:x)
#define mid (l+r>>1)
using namespace std;
const int mod=1000000007,W=11003;
int n,m,a[N],z[N];
vector<int> t[N];
ll k0,k1,pw[N];
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct tree2
{
    int rt[N],cnt,ch[N<<6][2],len[N<<6];
    ll w[N<<6];
    void update(int v)
    {
        w[v]=(w[ch[v][0]]*pw[len[v]>>1]+w[ch[v][1]])%mod;
    }
    void build(int v,int p,int l,int r,int x)
    {
        len[v]=r-l+1;
        if(l==r) {w[v]=w[p]+1;return;}
        if(x<=mid)
        {
            ch[v][1]=ch[p][1];
            ch[v][0]=++cnt;
            build(cnt,ch[p][0],l,mid,x);
        }
        else
        {
            ch[v][0]=ch[p][0];
            ch[v][1]=++cnt;
            build(cnt,ch[p][1],mid+1,r,x);
        }
        update(v);
    }
    int compare(int pl,int pr,int ql,int qr,int l,int r)
    {
        if(l==r) 
        {
            if(w[pr]-w[pl]==w[qr]-w[ql]) return 0;
            return (w[pr]-w[pl]<w[qr]-w[ql]?-1:1);
        }
        if((w[ch[pr][0]]-w[ch[pl][0]]+mod)%mod==(w[ch[qr][0]]-w[ch[ql][0]]+mod)%mod)
            return compare(ch[pl][1],ch[pr][1],ch[ql][1],ch[qr][1],mid+1,r);
        else
            return compare(ch[pl][0],ch[pr][0],ch[ql][0],ch[qr][0],l,mid);
    }
}T2;
struct node
{
    int l,r;
    bool operator <(node b)
    {
        int tmp=T2.compare(T2.rt[l-1],T2.rt[r],T2.rt[b.l-1],T2.rt[b.r],1,m);
        if(tmp==0) return l>b.l;
        return tmp<0;
    }
}ans[N];
struct tree1
{
    int lmi[N<<2],lmx[N<<2],rmi[N<<2],rmx[N<<2];
    ll sl[N<<2],sr[N<<2],sx[N<<2];
    void update(int v)
    {
        lmi[v]=min(lmi[v<<1],lmi[v<<1|1]);
        lmx[v]=max(lmx[v<<1],lmx[v<<1|1]);
        rmi[v]=min(rmi[v<<1],rmi[v<<1|1]);
        rmx[v]=max(rmx[v<<1],rmx[v<<1|1]);
        sl[v]=sl[v<<1]+sl[v<<1|1];
        sr[v]=sr[v<<1]+sr[v<<1|1];
        sx[v]=sx[v<<1]+sx[v<<1|1];
    }
    void pushdown(int v)
    {
        if(lmi[v]>lmi[v<<1])
            sl[v<<1]=sx[v<<1]*lmi[v],lmi[v<<1]=lmx[v<<1]=lmi[v];
        if(lmi[v]>lmi[v<<1|1])
            sl[v<<1|1]=sx[v<<1|1]*lmi[v],lmi[v<<1|1]=lmx[v<<1|1]=lmi[v];
        if(rmx[v]<rmx[v<<1])
            sr[v<<1]=sx[v<<1]*rmi[v],rmi[v<<1]=rmx[v<<1]=rmx[v];
        if(rmx[v]<rmx[v<<1|1])
            sr[v<<1|1]=sx[v<<1|1]*rmi[v],rmi[v<<1|1]=rmx[v<<1|1]=rmx[v];
    }
    void build(int v,int l,int r)
    {
        if(l==r)
        {
            lmi[v]=lmx[v]=sl[v]=l;
            rmi[v]=rmx[v]=sr[v]=n;
            sx[v]=1;
            return ;
        }
        build(v<<1,l,mid);
        build(v<<1|1,mid+1,r);
        update(v);
    }
    ll calc(int v,int l,int r,int lx,int rx,int x)
    {
        if(lx>rx||!sx[v]) return 0;
        if(l==lx&&r==rx)
        {
            if(lmi[v]>x) return 0;
            if(rmx[v]<=x) return sr[v]-sl[v]+sx[v];
            if(lmx[v]<=x&&rmi[v]>x) {return sx[v]*(x+1)-sl[v];}
        }
        pushdown(v);
        if(rx<=mid) return calc(v<<1,l,mid,lx,rx,x);
        if(lx>mid) return calc(v<<1|1,mid+1,r,lx,rx,x);
        return calc(v<<1,l,mid,lx,mid,x)+calc(v<<1|1,mid+1,r,mid+1,rx,x);
    }
    void mdf(int v,int l,int r,int lx,int rx,int lc,int rc)
    {
        if(lx>rx||!sx[v]) return ;

        if(l==lx&&r==rx)
        {
            bool flag=1;
            if(lc>rmx[v]||rc<lmi[v])
                {sx[v]=sl[v]=sr[v]=0;return ;}
            else if(lc>=lmx[v]&&rc<=rmi[v])
                sl[v]=sx[v]*lc,sr[v]=sx[v]*rc;
            else if(lc<=lmi[v]&&rc>=lmx[v]&&rc<=rmi[v])
                sr[v]=sx[v]*rc;
            else if(lc>=lmx[v]&&lc<=rmi[v]&&rc>=rmx[v])
                sl[v]=sx[v]*lc;
            else if(lc<=lmi[v]&&rc>=rmx[v])
                {}
            else flag=0;
            if(flag)
            {
                lmi[v]=max(lmi[v],lc);
                lmx[v]=max(lmx[v],lc);
                rmi[v]=min(rmi[v],rc);
                rmx[v]=min(rmx[v],rc);
                return ;
            }
        }
        pushdown(v);

        if(rx<=mid) mdf(v<<1,l,mid,lx,rx,lc,rc);
        else if(lx>mid) mdf(v<<1|1,mid+1,r,lx,rx,lc,rc);
        else mdf(v<<1,l,mid,lx,mid,lc,rc),mdf(v<<1|1,mid+1,r,mid+1,rx,lc,rc);
        update(v);
    }
    node find(int v,int l,int r,ll k)
    {
        if(l==r) return (node){l,lmi[v]};
        pushdown(v);
        if(k<=sx[v<<1|1]) return find(v<<1|1,mid+1,r,k);
        else return find(v<<1,l,mid,k-sx[v<<1|1]);
    }
}T1;
ll check(int c,int x)
{
    ll re=0;int sz=t[c].size();
    for(int i=1;i<sz-x;i++)
        re+=T1.calc(1,1,n,t[c][i-1]+1,min(t[c][i],n),t[c][i+x]-1);
    re+=T1.calc(1,1,n,t[c][sz-x-1]+1,n,n);
    return re;  
}
node solve0()
{
    for(int i=1;i<=m;i++)
    {
        int sz=t[i].size(),L=0,R=sz-2;

        while(L<R)
        {
            int Mid=(L+R>>1);
            if(check(i,Mid)<k0) L=Mid+1;
            else R=Mid;
        }
        if(L) k0-=check(i,L-1);

        for(int j=1;j<sz-L;j++)
            T1.mdf(1,1,n,t[i][j-1]+1,min(t[i][j],n),t[i][j+L-1],t[i][j+L]-1);   

        T1.mdf(1,1,n,t[i][sz-L-1]+1,n,n+1,n+1); 
    }
    return T1.find(1,1,n,k0);
}

struct gcmp
{
    bool operator() (node a,node b)
    {
        return b<a;
    }
};
priority_queue<node,vector<node>,gcmp > Q;
void solve1()
{
    T2.rt[0]=0;
    for(int i=1;i<=n;i++)
        T2.rt[i]=++T2.cnt,T2.build(T2.cnt,T2.rt[i-1],1,m,a[i]);

    for(int i=1;i<=n;i++)
    {
        int L=i,R=n;
        while(L<R)
        {
            int Mid=(L+R>>1);
            node p=(node){i,Mid};
            if(p<ans[1]) L=Mid+1;
            else R=Mid;
        }

        node q=(node){i,L};
        if(!(q<ans[1])) Q.push(q);
    }
    for(int i=1;i<=k1;i++)
    {
        ans[i]=Q.top();Q.pop();
        if(ans[i].r!=n) Q.push((node){ans[i].l,ans[i].r+1});
    }
}
int main()
{
    n=read();
    scanf("%lld%lld",&k0,&k1);
    k0=(ll)n*(n+1)/2-k0+1;
    k1=(ll)n*(n+1)/2-k1+1;
    swap(k0,k1);k1-=(k0-1);
    for(int i=1;i<=n;i++)
        z[i]=a[i]=read();

    sort(z+1,z+n+1);
    m=unique(z+1,z+n+1)-z-1;
    for(int i=1;i<=m;i++)
        t[i].push_back(0);
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(z+1,z+m+1,a[i])-z,t[a[i]].push_back(i);
    for(int i=1;i<=m;i++)
        t[i].push_back(n+1);

    T1.build(1,1,n);    
    ans[1]=solve0();

    pw[0]=1;
    for(int i=1;i<=m;i++)
        pw[i]=pw[i-1]*W%mod;
    solve1();
    for(int i=k1;i;i--)
        printf("%d %d\n",ans[i].l,ans[i].r);    

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值