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

来源:互联网 发布:centos debian 稳定性 编辑:程序博客网 时间:2024/06/04 21:48

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

对于前20% 的数据满足n <= 6
对于前50% 的数据满足n <= 2000
对于100% 的数据满足n <= 500000

Solution

  • 读入 p[i] ,转化为: q[p[i]]=iq[i] 表示值为 i 的位置在哪。

  • 考虑对于 q[i]q[j] ,若 |q[i]q[j]|<k (相距小于 k),则 q[i]q[j] 始终不能互换位置。

  • 于是 q[i]q[j] (i>j) 连一条有向边即可,这样就构成了一个有向无环图。

  • 将入度为 0 的点加入一个小根堆,做一遍拓扑排序。

  • 倒着枚举 i ,每次从堆里取出一个编号最小的 x

  • 使 p[i]=x ,再将 x 连出的点入度减一,为 0 则加入堆中。

  • 最后使 q[p[i]]=i , 再将 q 输出即可。

  • 为什么要维护一个堆呢?因为如果两点连了边(不能位置互换),就不会同时存在于堆中。

  • 在队中的点,肯定是可以位置互换的,那肯定是将小的数填在位置小那里,才能保证字典序最小。

-但是这种连边时间复杂度达到了 O(N2) ,这样就超时了。

  • 由于一个点只需与离它最近的点连边,维护一个维护最小值的线段树,

  • 倒着枚举 i ,每次查询 [q[i]k+1,q[i]][q[i],q[i]+k1] 两个区间的最小值

  • 即距离最近的两个点(一大一小),连边后再将 q[i] 这个位置单点修改成编号 i 即可。

  • 这样同样做一次拓扑排序,不会影响正确性,时间复杂度就成了 O(N log N)

Code

#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N=5e5+1;int tot;int a[N],b[N],f[N<<2];int first[N],next[N<<2],en[N<<2],d[N];bool bz[N];priority_queue<int,vector<int>,less<int> >q;inline int read(){    int X=0,w=1; char ch=0;    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();    return X*w;}inline void insert(int x,int y){    next[++tot]=first[x];    first[x]=tot;    en[tot]=y;    d[y]++;}inline void change(int v,int l,int r,int x,int y){    if(l==r)    {        f[v]=y;        return;    }    int mid=(l+r)>>1;    if(x<=mid) change(v<<1,l,mid,x,y); else change(v<<1|1,mid+1,r,x,y);    f[v]=min(f[v<<1],f[v<<1|1]);}inline int find(int v,int l,int r,int x,int y){    if(x<=l && r<=y) return f[v];    int mid=(l+r)>>1;    if(y<=mid) return find(v<<1,l,mid,x,y);    if(x>mid) return find(v<<1|1,mid+1,r,x,y);    return min(find(v<<1,l,mid,x,mid),find(v<<1|1,mid+1,r,mid+1,y));}int main(){    int n=read(),k=read();    for(int i=1;i<=n;i++) b[a[i]=read()]=i;    memset(f,60,sizeof(f));    for(int i=n;i;i--)    {        int x=find(1,1,n,b[i]-k+1,b[i]);        if(x<=n) insert(b[x],b[i]);        x=find(1,1,n,b[i],b[i]+k-1);        if(x<=n) insert(b[x],b[i]);        change(1,1,n,b[i],i);    }    for(int i=1;i<=n;i++)        if(!d[i]) q.push(i);    for(int i=n;i;i--)    {        a[i]=q.top();        q.pop();        for(int j=first[a[i]];j;j=next[j])            if(!--d[en[j]]) q.push(en[j]);    }    for(int i=1;i<=n;i++) b[a[i]]=i;     for(int i=1;i<=n;i++) printf("%d\n",b[i]);    return 0;}
阅读全文
1 0
原创粉丝点击