【题解】AT1984 Wide Swap(拓扑排序)
排列问题的一大套路是考虑arc数组(arc[data[t]]=t)
然后题目那个swap的性质,就转换为了对于arc数组相邻的值,若其差大于等于\(k\)则可以交换,否则不能。
考虑arc数组上任意两个数\(x,y\)(假设x的位置
"一定小于"关系会形成一个DAG,规定若\(y->x\)表示\(x\)位置上的数一定要比y的小。满足这个DAG的所有方案都是合法方案。一个生成合法方案的算法是在这个DAG上按拓扑一个个剥点,并且剥点时要分配递增的标号,这就相当于得到一个拓扑序,并且使得第\(1\)小的点尽量靠前,然后再满足第二大的尽量靠前...这就变成了HNOI菜肴制作了。
考虑复杂度的问题,暴力\(O(n^2)\)枚举不行的,可以发现若\(x,y\)不能交换且\(y,z\)不能交换,那么\(x,z\)不能交换(z的位置大于y的位置),所以只要连接一下那个下标最小的y就行了。
//@winlere
#include
#include
#include
#include
#include
#include
using namespace std; typedef long long ll;
inline int qr(){
int ret=0,f=0,c=getchar();
while(!isdigit(c))f|=c==45,c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=5e5+5;
const int inf=0x3f3f3f3f;
vector e[maxn];
int data[maxn],arc[maxn],dr[maxn],n,k;
int seg[maxn<<2],ans[maxn];
void pp(int pos){seg[pos]=min(seg[pos<<1],seg[pos<<1|1]);}
void add(int fr,int to){e[to].push_back(fr);++dr[fr];}
#define mid ((l+r)>>1)
#define lef l,mid,pos<<1
#define rgt mid+1,r,pos<<1|1
int que(int L,int R,int l,int r,int pos){
if(L>r||Rr) return;
if(l==r) return seg[pos]=v,void();
upd(p,v,lef); upd(p,v,rgt);
pp(pos);
}
#undef mid
#undef lef
#undef rgt
int main(){
n=qr(); k=qr();
for(int t=1;t<=n;++t) data[t]=qr(),arc[data[t]]=t;
memset(seg,0x3f,sizeof seg);
for(int t=n;t;--t){
int t1=que(arc[t]-k+1,arc[t]-1,1,n,1);
int t2=que(arc[t]+1,arc[t]+k-1,1,n,1);
if(t1<=n) add(arc[t],arc[t1]);
if(t2<=n) add(arc[t],arc[t2]);
upd(arc[t],t,1,n,1);
}
static priority_queue q;
for(int t=1;t<=n;++t)
if(!dr[t]) q.push(t);
int cnt=0;
while(q.size()){
int now=q.top();
ans[now]=++cnt;
q.pop();
for(auto t:e[now])
if(--dr[t]==0)
q.push(t);
}
for(int t=1;t<=n;++t) printf("%d\n",n-ans[t]+1);
return 0;
}