AGC001F-Wide Swap-拓扑排序

Wide Swap

Problem Statement

You are given a permutation P1…PN of the set {1, 2, …, N}.

You can apply the following operation to this permutation, any number of times (possibly zero):

Choose two indices i,j(1 ≦ i < j≦ N), such that j−i ≧ K and |Pi−Pj|=1. Then, swap the values of Pi and Pj.
Among all permutations that can be obtained by applying this operation to the given permutation, find the lexicographically smallest one.

Constraints

2≦N≦500,000
1≦K≦N−1
P is a permutation of the set {1, 2, …, N}.

Input

The input is given from Standard Input in the following format:

N K
P1 P2 … PN

Output

Print the lexicographically smallest permutation that can be obtained.

Sample Input 1

4 2
4 2 3 1

Sample Output 1

2
1
4
3

One possible way to obtain the lexicographically smallest permutation is shown below:

4231
4132
3142
2143

Sample Input 2

5 1
5 4 3 2 1

Sample Output 2

1
2
3
4
5

Sample Input 3

8 3
4 5 7 8 3 1 2 6

Sample Output 3

1
2
6
7
5
3
4
8

upd:
AtCoder上A了然而在某NOIP模拟赛中被卡常掉10pts……
于是新增01trie+链式前向星+priority_queue版……
原来的set+vector:1393ms
现在的01trie+链式前向星+priority_queue:808ms
从今以后贯彻常数优化的习惯……


真是巧妙啊……
果然AtCoder好题多……


题意:
给你一个序列,你可以任意交换两个距离大于等于k,且差的绝对值为1的数的位置,求可以实现的字典序最小的序列。

思路:
考虑到这个k很恶心,想办法让k变得舒服一点。
那么令 q 为原序列p的逆,也就是说, qpi=i
那么这就变成了可以交换两个相邻的,且值之差大于等于k的位置。

观察可以发现字典序最小的 q 对应字典序最小的p。(咱不会证只会观察)
然后差值小于k的数的相对位置不会改变。

然后变身套路题:优先队列拓扑排序。
如果差值小于k且 pi<pj 那么 i j连一条边。
第一个限制是因为拓扑序将使得其中一个必须在另一个之前入度归零。
第二个限制是出于字典序考虑。

考虑到这样的边数最坏是 O(n2) 的,那么优化~
可以发现对于每个点只与它左边和右边各一个比它大的最小的点连边就够了,其他边并不需要,因为这是拓扑关系,次序是一定的。

那么这就可以了~

set+vector版:

#include<iostream>
#include<set>
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

const int N=500009;

int n,k,ind[N];
int p[N],q[N];
set<int> f,b;
vector<int> g[N];

typedef set<int>::iterator it;

int main()
{
    n=read();
    k=read();

    for(int i=1;i<=n;i++)
        q[p[i]=read()]=i;
    for(int i=1;i<k;i++)
        f.insert(p[i]);
    for(int i=1;i<=n;i++)
    {
        if(i+k-1<=n)
            f.insert(p[i+k-1]);
        if(i)
            f.erase(p[i-1]);

        b.insert(p[i]);
        if(i>k)
            b.erase(p[i-k]);

        it cur=f.upper_bound(p[i]);
        if(cur!=f.end())
            g[q[*cur]].push_back(i),ind[i]++;
        cur=b.upper_bound(p[i]);
        if(cur!=b.end())
            g[q[*cur]].push_back(i),ind[i]++;
    }

    f.clear();
    for(int i=1;i<=n;i++)
        if(!ind[i])
            f.insert(-i);

    int top=0;
    while(f.size())
    {
        int u=-(*f.begin());
        p[++top]=u;
        f.erase(f.begin());
        for(int i=0,ed=g[u].size();i<ed;i++)
            if(--ind[g[u][i]]==0)
                f.insert(-g[u][i]);
    }

    for(int i=1;i<=n;i++)
        q[p[i]]=n-i+1;
    for(int i=1;i<=n;i++)
        printf("%d\n",q[i]);
    return 0;
}

01trie+链式前向星+priority_queue:
(01trie还是比平衡树系列好写一些啊……同时在长度和无脑程度上)

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

const int N=500009;
const int K=25;  

int n,k,ind[N];
int p[N],q[N];
int ch[N*K][2],cnt[N*K],tot;
int to[N<<1],nxt[N<<1],beg[N],tots;
priority_queue<int,vector<int>,greater<int> > f;

inline void adde(int u,int v)
{
    to[++tots]=v;
    nxt[tots]=beg[u];
    beg[u]=tots;
}

inline void add(int x,int v)
{
    int now=0;
    cnt[now]+=v;
    for(int i=K,nxt;i>=0;i--)
    {
        if(!ch[now][(nxt=((x>>i)&1))])
            ch[now][nxt]=++tot;
        now=ch[now][nxt];
        cnt[now]+=v;
    }
}

inline int ranks(int x)
{
    int now=0,ret=0;
    for(int i=K,nxt;i>=0;i--)
    {
        if((nxt=((x>>i)&1)) && ch[now][0])
            ret+=cnt[ch[now][0]];
        now=ch[now][nxt];
    }
    return ret+1;
}

inline int query(int kth)
{
    if(kth>cnt[0])return -1;
    int now=0,ret=0,f=0;
    for(int i=K,nxt;i>=0;i--)
    {
        if(ch[now][0] && cnt[ch[now][0]]>=kth)
            nxt=0;
        else
        {
            nxt=1;
            if(ch[now][0])
                kth-=cnt[ch[now][0]];
            ret|=(1<<i);
        }
        now=ch[now][nxt];
    }
    return ret;
}

int main()
{
    if(fopen("data.in","r"))
    {
        freopen("data.in","r",stdin);
        freopen("data.out","w",stdout);
    }

    n=read();
    k=read();

    for(int i=1;i<=n;i++)
        q[p[i]=read()]=i;

    memset(ch,0,sizeof(ch));
    memset(cnt,0,sizeof(cnt));
    tot=0;

    for(int i=1;i<k;i++)
        add(p[i],1);
    for(int i=1;i<=n;i++)
    {
        if(i+k-1<=n)
            add(p[i+k-1],1);
        if(i>1)
            add(p[i-1],-1);

        int las=query(ranks(p[i])+1);
        if(las!=-1)
            adde(q[las],i),ind[i]++;
    }

    memset(ch,0,sizeof(ch));
    memset(cnt,0,sizeof(cnt));
    tot=0;

    for(int i=1;i<=n;i++)
    {
        add(p[i],1);
        if(i>k)
            add(p[i-k],-1);

        int las=query(ranks(p[i])+1);
        if(las!=-1)
            adde(q[las],i),ind[i]++;
    }

    for(int i=1;i<=n;i++)
        if(!ind[i])
            f.push(-i);

    int top=0;
    while(!f.empty())
    {
        int u=-f.top();
        p[++top]=u;
        f.pop();
        for(int i=beg[u];i;i=nxt[i])
            if(--ind[to[i]]==0)
                f.push(-to[i]);
    }

    for(int i=1;i<=n;i++)
        q[p[i]]=n-i+1;
    for(int i=1;i<=n;i++)
        printf("%d\n",q[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值