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

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

题解

因为有|Pi-Pj| = 1,意思就是只有P的值是相邻的才能交换。
我们构造一个新的序列: qpi=i
那么在q序列中,可以交换的只有相邻的两个,
qiqi+1 才有可能可以进行交换。
而且, |qiqi+1|>=k
如果对于1..i中,都满足 |qiqi1|>=k ,那么i就可以被一直交换的1这个位置。
如果,在i的前面的某个位置j,不满足 |qjqj1|>=k
那么,i最多只能被交换到j的位置。
这就说明了它们的位置的相对顺序是不会改变的。

根据这个相对顺序来连边,
然后跑一边拓扑序,注意一下最小值就可以了。

code

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 500003
#define db double
#define P putchar
#define G getchar
#define mo 998244353
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

db max(db a,db b){return a>b?a:b;}
db min(db a,db b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

struct node{int x;}t;

priority_queue <node> d;
bool operator <(node a,node b)
{
    return a.x<b.x;
}
int n,k,ans[N],x,p[N],q[N];
int nxt[N*2],f[N],to[N*2],b[N],tot;
int tr[N*4],opl,opr,opx,ops;

void work(int x,int l,int r)
{
    if(opl<=l && r<=opr)
    {
        if(opx==1)ops=min(ops,tr[x]);
        if(opx==2)tr[x]=ops;
        return;
    }
    int m=(l+r)>>1;
    if(opl<=m)work(x+x,l,m);
    if(m<opr)work(x+x+1,m+1,r);
    tr[x]=min(tr[x+x],tr[x+x+1]);
}

void ins(int x,int y)
{
    nxt[++tot]=b[x];
    to[tot]=y;
    b[x]=tot;
    f[y]++;
}

int main()
{
    freopen("permutation.in","r",stdin);
    freopen("permutation.out","w",stdout);
    read(n);read(k);memset(tr,127,sizeof(tr));
    for(int i=1;i<=n;i++)
        read(p[i]),q[p[i]]=i;

    for(int i=n;i;i--)
    {
        opl=q[i]-k+1;opr=q[i];opx=1;ops=n+1;
        work(1,1,n);
        if(ops<n+1)ins(q[ops],q[i]);

        opl=q[i];opr=q[i]+k-1;opx=1;ops=n+1;
        work(1,1,n);
        if(ops<n+1)ins(q[ops],q[i]);

        opl=q[i];opr=q[i];opx=2;ops=i;
        work(1,1,n);
    }

    for(int i=1;i<=n;i++)
        if(f[i]==0)t.x=i,d.push(t);

    for(int i=n;i;i--)
    {
        t=d.top();d.pop();x=t.x;p[i]=x;
        for(int i=b[x];i;i=nxt[i])
        {
            f[to[i]]--;
            if(f[to[i]]==0)t.x=to[i],d.push(t);
        }
    }

    for(int i=1;i<=n;i++)
        q[p[i]]=i;
    for(int i=1;i<=n;i++)
        write(q[i]),P('\n');

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值