[JZOJ5405]【NOIP2017提高A组模拟10.10】Permutation

来源:互联网 发布:js上传图片并预览 编辑:程序博客网 时间:2024/06/04 17:41

Description

你有一个长度为n 的排列P 与一个正整数K
你可以进行如下操作若干次使得排列的字典序尽量小
对于两个满足|i-j|>=K 且|Pi-Pj| = 1 的下标i 与j,交换Pi 与Pj

对于100% 的数据满足n <= 500000

Solution

隔着K个数之间交换比较麻烦,不如变换一下题意

原序列记录每个位置上的数什么
不妨新开一个数组Q记录每个数在哪个位置。

交换只是相邻两个交换,只要相邻两个数的差的绝对值大于等于K就可以交换。
排列P的字典序最小,与数组Q的字典序最小是等价的。

有了这个结论,就可以直接在数组Q上做了。

下面的值都是数组Q上的值
显然,如果|xy|<K,那么x,y的相对位置无论怎么换,x这个数总是在y前面的。

那么就有了一种N2的做法,对于每一对|Q[i]Q[j]|<K,i<j,把Q[i]Q[j]连边,构成一个DAG,那么构造出来的最小字典序的拓扑序就是最后换出来的Q,再还原成P即可。

最小字典序的拓扑序可以维护一个堆,每次取堆顶加入拓扑序,扩展节点,新走出的节点入度为0时加入堆即可。

然而可以发现,这样连边会有大量不必要的边。
对于Q[x],Q[y],Q[z],x<y<z
如果|Q[x]Q[y]|<K,|Q[y]Q[z]|<K,|Q[x]Q[z]|<K

那么只需要Q[x]连Q[y],Q[y]连Q[z]即可。

那么从后往前扫,对于当前扫到的Q[i],只需要找到i后面最小的x,y,使得
Q[x][Q[i]K+1,Q[i]1],Q[y][Q[i]+1,Q[i]+K1]

Q[i]分别向Q[x],Q[y]连边即可

这个东西可以维护一个权值线段树,从后向前扫的时候插入即可。

Code

#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#include <iostream>#include <cstdlib>#include <queue>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fod(i,a,b) for(int i=a;i>=b;i--)#define N 500005#define INF 2147483647using namespace std;int n,w[N],m,m1,n1,fs[N],nt[2*N],dt[2*N],rd[N],d[N],as[N],a1[N];priority_queue <int> a;void link(int x,int y){    nt[++m1]=fs[x];    rd[dt[fs[x]=m1]=y]++;}struct node{    int s,l,r,s1;}tr[5*N];void up(int k){    tr[k].s=min(tr[tr[k].l].s,tr[tr[k].r].s);    tr[k].s1=max(tr[tr[k].l].s1,tr[tr[k].r].s1);}void ins(int k,int l,int r,int x,int v){    if(l==r) tr[k].s=v,tr[k].s1=v;    else    {        int mid=(l+r)/2;        if(!tr[k].l) tr[k].l=++n1,tr[n1].s=INF;        if(!tr[k].r) tr[k].r=++n1,tr[n1].s=INF;        if(x<=mid) ins(tr[k].l,l,mid,x,v);        else ins(tr[k].r,mid+1,r,x,v);        up(k);    }}int fd(int k,int l,int r,int x,int y){    x=max(x,l),y=min(y,r);    if(x>y||k==0) return INF;    if(l==x&&r==y) return tr[k].s;    int mid=(l+r)/2;    return min(fd(tr[k].l,l,mid,x,y),fd(tr[k].r,mid+1,r,x,y));}int fd2(int k,int l,int r,int x,int y){    x=max(x,l),y=min(y,r);    if(x>y||k==0) return 0;    if(l==x&&r==y) return tr[k].s1;    int mid=(l+r)/2;    return max(fd2(tr[k].l,l,mid,x,y),fd2(tr[k].r,mid+1,r,x,y));}void doit(){    int l=0,r=d[0];    fo(i,1,n) if(rd[i]==0) a.push(-i);    while(r<n)    {        int k=-a.top();        a.pop();        d[++r]=k;        for(int i=fs[k];i;i=nt[i])        {            int p=dt[i];            rd[p]--;            if(rd[p]==0) a.push(-p);        }    }}int main(){    cin>>n>>m;    n1=1;    fo(i,1,n)    {        scanf("%d",&a1[i]);        w[a1[i]]=i;    }    ins(1,1,n,w[n],n);    fod(i,n-1,1)    {        int p=fd(1,1,n,w[i],w[i]+m-1),q=fd(1,1,n,w[i]-m+1,w[i]);        if(p!=INF) link(w[i],w[p]);        if(q!=INF) link(w[i],w[q]);        ins(1,1,n,w[i],i);    }    doit();    fo(i,1,n) as[d[i]]=i;    fo(i,1,n) printf("%d\n",as[i]);}