蓝雨

题目大意

一个长度为 n n 的序列,每个元素都是[1,n]
一个区间代表的序列是将其元素排序。
现在求出区间代表序列字典序按高到低排名在区间 [p,q] [ p , q ] 的区间。
q-p<=100000。

做法

我们如何快速比较两个区间代表序列的字典序?
主席树维护区间哈希即可。
那么只要我知道第p个,可以用经典做法推到第q个。
关键是第p个。
考虑逐位确定,每位二分有多少个。
已经确定了一些位后,每个左端点的合法右端点是一段区间。
线段树维护即可。

#include<cstdio>
#include<algorithm>
#include<queue>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const int maxn=100000+10,maxtot=10000000+10,mo1=1000000007,mo2=998244353;
int root[maxn],tree[maxtot][2],left[maxtot],right[maxtot],ci[maxtot],cm[maxn][2];
ll num[maxn*4],sum[maxn*4];
int mi[maxn*4],mx[maxn*4],mim[maxn*4],mxm[maxn*4],st[maxn*4],rs[maxn*4],cnt[maxn];
bool bz[maxn*4],pd[maxn*4];
int last[maxn],lft[maxn],sta[maxn],a[maxn];
int i,j,k,l,r,lc,rc,mid,t,n,m,tot,top;
ll p,q,wdc,xdl,zjy;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
pi getha(int x,int y){
    int j=tree[y][0]-tree[x][0];
    (j+=mo1)%=mo1;
    int k=tree[y][1]-tree[x][1];
    (k+=mo2)%=mo2;
    return mp(j,k);
}
int newnode(int x){
    tot++;
    tree[tot][0]=tree[x][0];
    tree[tot][1]=tree[x][1];
    left[tot]=left[x];
    right[tot]=right[x];
    ci[tot]=ci[x];
    return tot;
}
void insert(int &x,int l,int r,int a){
    x=newnode(x);
    (tree[x][0]+=cm[a-1][0])%=mo1;
    (tree[x][1]+=cm[a-1][1])%=mo2;
    if (l==r){
        ci[x]++;
        return;
    }
    int mid=(l+r)/2;
    if (a<=mid) insert(left[x],l,mid,a);else insert(right[x],mid+1,r,a);
}
int compare(int x1,int y1,int x2,int y2,int l,int r){
    if (l==r){
        int j=ci[y1]-ci[x1],k=ci[y2]-ci[x2];
        if (j==k) return 0;
        else if (j<k) return -1;
        else return 1;
    }
    if (getha(x1,y1)==getha(x2,y2)) return 0;
    int mid=(l+r)/2;
    if (getha(left[x1],left[y1])==getha(left[x2],left[y2])) return compare(right[x1],right[y1],right[x2],right[y2],mid+1,r);
    else return compare(left[x1],left[y1],left[x2],left[y2],l,mid);
}
bool cmp(int l1,int r1,int l2,int r2){
    int t=compare(root[l1-1],root[r1],root[l2-1],root[r2],1,n);
    if (t==-1) return 1;
    else if (t==0&&l1>l2) return 1;
    else return 0;
}
struct dong{
    int l,r;
    friend bool operator <(dong a,dong b){
        return cmp(a.l,a.r,b.l,b.r);
    }
} zlt;
priority_queue<dong> heap;
void build(int p,int l,int r){
    if (l==r){
        num[p]=mx[p]=mim[p]=l;
        sum[p]=mi[p]=mxm[p]=n;
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    num[p]=num[p*2]+num[p*2+1];
    sum[p]=sum[p*2]+sum[p*2+1];
    mi[p]=mi[p*2];
    mim[p]=mim[p*2];
    mx[p]=mx[p*2+1];
    mxm[p]=mxm[p*2+1];
}
void markbz(int p,int l,int r,int x){
    bz[p]=1;
    st[p]=x;
    mx[p]=mim[p]=x;
    num[p]=(ll)(r-l+1)*x;
}
void markpd(int p,int l,int r,int x){
    pd[p]=1;
    rs[p]=x;
    mi[p]=mxm[p]=x;
    sum[p]=(ll)(r-l+1)*x;
}
void down(int p,int l,int r){
    int mid=(l+r)/2;
    if (bz[p]){
        markbz(p*2,l,mid,st[p]);
        markbz(p*2+1,mid+1,r,st[p]);
        bz[p]=0;
    }
    if (pd[p]){
        markpd(p*2,l,mid,rs[p]);
        markpd(p*2+1,mid+1,r,rs[p]);
        pd[p]=0;
    }
}
ll query(int p,int l,int r,int a,int b,int x){
    if (l==r){
        if (mi[p]<x) return 0;
        else if (mx[p]<x) return mi[p]-x+1;
        else return mi[p]-mx[p]+1;
    }
    down(p,l,r);
    if (l==a&&r==b){
        int mid=(l+r)/2;
        if (mi[p]<x){
            if (mi[p*2+1]<x) return query(p*2+1,mid+1,r,mid+1,b,x);
            else return query(p*2,l,mid,a,mid,x)+query(p*2+1,mid+1,r,mid+1,b,x);
        }
        else if (mx[p*2]<x) return sum[p*2]-(ll)(x-1)*(mid-l+1)+query(p*2+1,mid+1,r,mid+1,b,x);
        else return query(p*2,l,mid,a,mid,x)+sum[p*2+1]-num[p*2+1]+(r-mid);
    }
    int mid=(l+r)/2;
    if (b<=mid) return query(p*2,l,mid,a,b,x);
    else if (a>mid) return query(p*2+1,mid+1,r,a,b,x);
    else return query(p*2,l,mid,a,mid,x)+query(p*2+1,mid+1,r,mid+1,b,x);
}
void reset(int p,int l,int r,int a,int b,int j,int s){
    if (mim[p]==mxm[p]+1){
        if (mi[p]<j){
            markbz(p,l,r,j);
            markpd(p,l,r,j-1);
        }
        else if (mx[p]>s){
            markbz(p,l,r,s+1);
            markpd(p,l,r,s);
        }
        return;
    }
    //return;
    if (l==r){
        if (mi[p]<j){
            markbz(p,l,r,j);
            markpd(p,l,r,j-1);
        }
        else if (mx[p]>s){
            markbz(p,l,r,s+1);
            markpd(p,l,r,s);
        }
        else{
            if (mx[p]<j) markbz(p,l,r,j);
            if (mi[p]>s) markpd(p,l,r,s);
        }
        return;
    }
    down(p,l,r);
    if (l==a&&r==b){
        int mid=(l+r)/2;
        if (mi[p]<j){
            if (mi[p*2+1]<j){
                markbz(p*2,l,mid,j);
                markpd(p*2,l,mid,j-1);
                reset(p*2+1,mid+1,r,mid+1,b,j,s);
            }
            else{
                reset(p*2,l,mid,a,mid,j,s);
                reset(p*2+1,mid+1,r,mid+1,b,j,s);
            }
        }
        else if (mx[p]>s){
            if (mx[p*2]>s){
                markbz(p*2+1,mid+1,r,s+1);
                markpd(p*2+1,mid+1,r,s);
                reset(p*2,l,mid,a,mid,j,s);
            }
            else{
                reset(p*2,l,mid,a,mid,j,s);
                reset(p*2+1,mid+1,r,mid+1,b,j,s);
            }
        }
        else{
            bool czy=0,gjx=0;
            if (mx[p*2]<j){
                markbz(p*2,l,mid,j);
                gjx=1;
            }
            else czy=1;
            if (mi[p*2+1]>s){
                markpd(p*2+1,mid+1,r,s);
                czy=1;
            }
            else gjx=1;
            //return;
            if (czy) reset(p*2,l,mid,a,mid,j,s);
            //return;
            if (gjx) reset(p*2+1,mid+1,r,mid+1,b,j,s);
        }
        num[p]=num[p*2]+num[p*2+1];
        sum[p]=sum[p*2]+sum[p*2+1];
        mi[p]=mi[p*2];
        mim[p]=mim[p*2];
        mx[p]=mx[p*2+1];
        mxm[p]=mxm[p*2+1];
        return;
    }
    int mid=(l+r)/2;
    if (b<=mid) reset(p*2,l,mid,a,b,j,s);
    else if (a>mid) reset(p*2+1,mid+1,r,a,b,j,s);
    else{
        reset(p*2,l,mid,a,mid,j,s);
        reset(p*2+1,mid+1,r,mid+1,b,j,s);
    }
    num[p]=num[p*2]+num[p*2+1];
    sum[p]=sum[p*2]+sum[p*2+1];
    mi[p]=mi[p*2];
    mim[p]=mim[p*2];
    mx[p]=mx[p*2+1];
    mxm[p]=mxm[p*2+1];
}
ll check(int x,int k){
    int i,j,l,r;
    ll t=0;
    fo(i,k,top){
        l=sta[i+1]+1;r=sta[i];
        j=sta[i-k+1];
        t+=query(1,1,n,l,r,j);
    }
    return t;
}
void modify(int x,int k){
    if (k){
        reset(1,1,n,sta[k]+1,n,n+1,n);
    }
    int i,j,s,l,r;
    fo(i,max(k,1),top){
        l=sta[i+1]+1;r=sta[i];
        j=sta[i-k+1];s=sta[i-k]-1;
        reset(1,1,n,l,r,j,s);
    }
}
void travel(int p,int l,int r){
    if (l==r){
        //printf("%d %d\n",mx[p],mi[p]);
        if (mx[p]==mi[p]){
            wdc--;
            if (!wdc){
                lc=l;
                rc=mx[p];
            }
        }
        return;
    }
    down(p,l,r);
    int mid=(l+r)/2;
    travel(p*2,l,mid);
    travel(p*2+1,mid+1,r);
}
int main(){
    freopen("data.in","r",stdin);freopen("data.out","w",stdout);
    n=read();
    cm[0][0]=cm[0][1]=1;
    fo(i,1,n){
        cm[i][0]=(ll)cm[i-1][0]*(n+1)%mo1;
        cm[i][1]=(ll)cm[i-1][1]*(n+1)%mo2;
    }
    scanf("%lld%lld",&p,&q);
    fo(i,1,n){
        a[i]=read();
        cnt[a[i]]++;
        lft[i]=last[a[i]];
        last[a[i]]=i;
        root[i]=root[i-1];
        insert(root[i],1,n,a[i]);
    }
    build(1,1,n);
    wdc=p;
    fo(i,1,n){
        top=0;
        j=last[i];
        while (j){
            sta[++top]=j;
            j=lft[j];
        }
        sta[0]=n+1;sta[top+1]=0;
        xdl=check(i,1);
        if (xdl<wdc){
            wdc-=xdl;
            modify(i,0);
            continue;
        }
        l=1;r=cnt[i]+1;
        /*while (l<r){
            mid=(l+r)/2;
            if (check(i,mid)<wdc) r=mid;else l=mid+1;
        }*/
        while (l<=r){
            mid=(l+r)/2;
            zjy=check(i,mid);
            if (zjy<wdc){
                k=mid;
                xdl=zjy;
                r=mid-1;
            }
            else l=mid+1;
        }
        wdc-=xdl;
        //k=l-1;
        k--;
        modify(i,k);
    }
    travel(1,1,n);
    j=n;
    fo(i,1,n){
        if (i==lc){
            j=rc;
            zlt.l=i;zlt.r=j;
            heap.push(zlt);
            continue;
        }
        while (!cmp(i,j,lc,rc)) j--;
        while (j<n&&cmp(i,j+1,lc,rc)) j++;
        zlt.l=i;zlt.r=j;
        heap.push(zlt);
    }
    /*fo(i,1,n){
        zlt.l=i;zlt.r=n;
        heap.push(zlt);
    }*/
    wdc=q-p+1;
    while (wdc--){
        zlt=heap.top();
        heap.pop();
        printf("%d %d\n",zlt.l,zlt.r);
        if (zlt.r>zlt.l){
            zlt.r--;
            heap.push(zlt);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值