JZOJ 5405.【NOIP2017提高A组模拟10.10】Permutation

来源:互联网 发布:北京外语网络教育官网 编辑:程序博客网 时间:2024/05/16 06:26

题目

Description
你有一个长度为n 的排列P 与一个正整数K
你可以进行如下操作若干次使得排列的字典序尽量小
对于两个满足|i-j|>=K 且|Pi-Pj| = 1 的下标i 与j,交换Pi 与Pj
Input
第一行包括两个正整数n 与K
第二行包括n 个正整数,第i 个正整数表示Pi
Output
输出一个新排列表示答案
输出共n 行,第i 行表示Pi
Sample Input
8 3
4 5 7 8 3 1 2 6
Sample Output
1
2
6
7
5
3
4
8
Data Constraint
对于100% 的数据满足n <= 500000

题解

在比赛的过程中,我发现p[i]变成x,当前仅当条件:x和p[i]之间的所有数,p[i]都与他们距离≥k。
为了方便解决此题,我们将问题转化为:q[p[i]]=i,然后对q序列解决问题。那么两个数能移动,当且仅当相邻且|q[i]q[i1]|k.
所以,我们只要找到与i相关的两个数j1,j2>i,
j1[q[i]k+1,q[i]]j2[q[i],q[i]+k1]。然后q[i]向q[j1],q[j2]各连一条边,
表示j1无论怎么只靠跟后面换,都不能够换到i前面(若j1可以跟前面换则不优)。j2同理
即表示,最后i表示的数在j1,j2前面。

这样子我们搞出了一个DAG。
由于题目要求输出最小字典序的序列,所以保证拓扑序字典序最小即可。
解决方案:用优先队列存储即可,每次让最小的出队就行了。
然而C++的priority_queue默认为“大根堆”。“小根堆”怎么办?
解决方案:将加进优先队列的数取相反数,然后取出来的时候再去相反数。
对这道题目的心得:
①不要将数列上的数看得太死,要让他们在脑中”动起来”。
②一道题目这么难,肯定有突破口,比如这题的一定交换相邻的两个数,这两个数绝对值等于1!!!所以可以想到一直交换,直到有一个交换不到的数就截止。
③最重要的一点,对于这种序列(这种序列是排列)的问题,如果涉及到要利用”每个数的位置”来解决问题,就可以试下考虑逆数组。即q[p[i]]=i。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#define N 500010#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;struct note{    int to,next;};note edge[N*2];int p[N],q[N];int tr[N*20],ru[N];int i,j,k,L,R,l,r,n,temp,now,ans,step;int tot,head[N];priority_queue<int>qu;bool pp;void read(int &n){    n=0;char ch=getchar();    while((ch<'0' || ch>'9') && ch!='-')ch=getchar();    int w=1;    if(ch=='-')w=-1,ch=getchar();    while('0'<=ch && ch<='9')n=n*10+ch-'0',ch=getchar();    n*=w;}void write(int x){    if(x>9)write(x/10);    putchar(x%10+'0');}void lb(int x,int y){    edge[++tot].to=y;    edge[tot].next=head[x];    head[x]=tot;    ru[y]++;}void update(int ps){    tr[ps]=min(tr[ps*2],tr[ps*2+1]);}void change(int ps,int l,int r,int x,int z){    if(l==r){        tr[ps]=z;        return;    }    int wz=(l+r)/2;    if(x<=wz)change(ps*2,l,wz,x,z);        else change(ps*2+1,wz+1,r,x,z);    update(ps);}void find(int ps,int l,int r,int x,int y){    if(l==x && r==y){        temp=min(temp,tr[ps]);        return;    }    int wz=(l+r)/2;    if(y<=wz)find(ps*2,l,wz,x,y);else    if(x>wz)find(ps*2+1,wz+1,r,x,y);else{        find(ps*2,l,wz,x,wz);        find(ps*2+1,wz+1,r,wz+1,y);    } }int main(){    freopen("permutation.in","r",stdin);    freopen("permutation.out","w",stdout);    read(n),read(k);    fo(i,1,n)read(p[i]),q[p[i]]=i;    memset(tr,127,sizeof(tr));    fd(i,n,1){        L=max(1,q[i]-k+1);R=q[i];temp=n+1;        find(1,1,n,L,R);        if(temp<=n)lb(q[i],q[temp]);        L=q[i];R=min(n,q[i]+k-1);temp=n+1;        find(1,1,n,L,R);        if(temp<=n)lb(q[i],q[temp]);        change(1,1,n,q[i],i);    }    fo(i,1,n)if(!ru[i]) qu.push(-i);    memset(q,0,sizeof(q));    fo(i,1,n){        int now=qu.top();        now=-now;        q[i]=now;        qu.pop();        for(j=head[now];j;j=edge[j].next){            ru[edge[j].to]--;            if(!ru[edge[j].to])qu.push(-edge[j].to);        }    }    fo(i,1,n)p[q[i]]=i;    fo(i,1,n)write(p[i]),putchar('\n');    return 0;}
原创粉丝点击