AtCoder Grand Contest 001F Wide Swap

解法参考这位大佬的:https://www.cnblogs.com/BearChild/p/7895719.html

 因为原来的数组不好做于是我们想反过来数组,根据交换条件:值相邻且位置差大于等于k,那么在变换后的数组就变成了位置相邻且差值大于等于k。这样的话变换操作变成了,相邻的大于等于k的值临近交换,于是我们注意到因为现在只能临近交换的原因,两个差值小于k的数他们的相对位置不可能发生改变。那么问题就变成了,在只有一些相对位置限制条件下,无限制的可以随意交换位置,求这个数组的最小字典序(原数组字典序最小也是现在数组字典序最小)。那么我们容易想到拓扑排序。

但是如果每个点都想后面差值小于k的点连边的话,这个图会变得十分巨大,时间无法承受。于是我们必循得考虑优化建图:我们注意到像a->b,b->c,a->c这种建图,a->c这条边是不必要的。于是我们想办法避免掉这种无意义的边,所以对于某个点,我们让它向后面的所有限制(即差值小于k)中只向最小的那一个点连边,那么用线段树维护这样的信息,这样就达到优化建图的目的。

这样只向最小的连边为什么是对的呢?借用上面大佬的一句话:倒着加入,显然 p_i 连向 (p_i-k, p_i)∪(p_i, p_i+k)。我们只需要分别连向两个区间中下标最小的那一个即可。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
const int INF=0x3f3f3f3f;
int n,k,tot,a[N],pos[N],deg[N],ans[N];
set<int> L,R;
vector<int> G[N];

priority_queue<int> q;
void toposort() {
    for (int i=1;i<=n;i++)
        if (deg[i]==0) q.push(-i);
    while (!q.empty()) {
        int x=-q.top(); q.pop();
        a[++tot]=x;
        for (int i=0;i<G[x].size();i++) {
            int y=G[x][i];
            if (--deg[y]==0) q.push(-y);
        }
    }    
}

int Min[N<<2];
void build(int rt,int l,int r) {
    Min[rt]=INF;
    if (l==r) return;
    int mid=l+r>>1;
    build(rt<<1,l,mid); build(rt<<1|1,mid+1,r);
}

void update(int rt,int l,int r,int q,int v) {
    if (l==r) { Min[rt]=min(Min[rt],v); return; }
    int mid=l+r>>1;
    if (q<=mid) update(rt<<1,l,mid,q,v);
    if (q>mid) update(rt<<1|1,mid+1,r,q,v); 
    Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
}

int query(int rt,int l,int r,int ql,int qr) {
    if (ql<=l && r<=qr) return Min[rt];
    int mid=l+r>>1;
    int ret=INF;
    if (ql<=mid) ret=min(ret,query(rt<<1,l,mid,ql,qr));
    if (qr>mid) ret=min(ret,query(rt<<1|1,mid+1,r,ql,qr));
    return ret; 
}

int main()
{
    cin>>n>>k;
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=n;i++) pos[a[i]]=i;
    
    build(1,1,n);
    for (int i=n;i;i--) {
        int t1=query(1,1,n,max(1,pos[i]-k+1),pos[i]);
        if (t1<=n) G[pos[i]].push_back(pos[t1]),deg[pos[t1]]++;
        int t2=query(1,1,n,pos[i],min(n,pos[i]+k-1));
        if (t2<=n) G[pos[i]].push_back(pos[t2]),deg[pos[t2]]++;
        update(1,1,n,pos[i],i);
    }
    
    toposort();
    for (int i=1;i<=n;i++) ans[a[i]]=i;  //最后记得把答案反过来 
    for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/clno1/p/10832579.html