AGC001F-Wide Swap-拓扑排序

来源:互联网 发布:mac误删文件怎么恢复 编辑:程序博客网 时间:2024/05/01 13:41

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 KP1 P2 … PN

Output

Print the lexicographically smallest permutation that can be obtained.

Sample Input 1

4 24 2 3 1

Sample Output 1

2143

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

4231
4132
3142
2143

Sample Input 2

5 15 4 3 2 1

Sample Output 2

12345

Sample Input 3

8 34 5 7 8 3 1 2 6

Sample Output 3

12675348

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那么ij连一条边。
第一个限制是因为拓扑序将使得其中一个必须在另一个之前入度归零。
第二个限制是出于字典序考虑。

考虑到这样的边数最坏是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;}
原创粉丝点击