【AGC001F】Wide Swap(优化建图,最小拓扑序)

题面

P P P 为一个 1 到 n 的排列,对于 1 ≤ i < j ≤ n 1\leq i<j\leq n 1i<jn,如果有 j − i ≥ K j-i\geq K jiK ∣ P i − P j ∣ = 1 |P_i-P_j|=1 PiPj=1,那么可以将其交换。

求若干次交换后,字典序最小的排列。

n ≤ 5 × 1 0 5 . n\leq 5\times 10^5. n5×105.

题解

我们令 P P P 的逆序列为 Q Q Q Q P i = i Q_{P_i}=i QPi=i),那么,把交换操作映射到 Q Q Q 上,就变成了可以交换相邻两个“差的绝对值不小于 K K K ”的数

如果两个数之差小于 K K K ,那么它们的前后相对位置一定固定,因为当它们相邻时无法交换位置。

我们把这种关系表示为从前到后的一条有向边,那么,当我们把这个图建出来的时候,答案序列就是每个点拓扑序序列的最小序列了。

原图直接建出来是不可能的,但是我们发现有大量无价值的边,于是可以优化建图。不难证明,我们只要保留满足 i < j , Q i < Q j < Q i + K i<j,Q_i<Q_j<Q_i+K i<j,Qi<Qj<Qi+K i < j , Q i − K < Q j < Q i i<j,Q_i-K<Q_j<Q_i i<j,QiK<Qj<Qi 、同时 j j j 最小的边 Q i → Q j Q_i\rightarrow Q_j QiQj 即可。

对于最小拓扑序序列,指的是把结点 1 , 2 , 3 , . . . 1,2,3,... 1,2,3,... 的拓扑序依次排成一个序列,满足字典序最小。这道题的 P P P 就是拓扑序序列,而 Q Q Q拓扑序列。这种最小拓扑序序列有经典解法:

对于原 DAG 建反图,然后求出该反图的最大拓扑序列 Q ′ Q' Q ,然后令 P Q i ′ = n − i + 1 P_{Q'_i}=n-i+1 PQi=ni+1

证明略。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

CODE

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 500005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

const int MOD = 1000000007;
int n,m,s,o,k;
int a[MAXN],ad[MAXN];
int tre[MAXN<<2],M;
void maketree(int n) {
	M=1;while(M<n+2)M<<=1;
	memset(tre,0x3f,sizeof(tre));
}
void addtree(int x,int y) {
	int s = M+x; tre[s] = y; s >>= 1;
	while(s) tre[s] = min(tre[s<<1],tre[s<<1|1]),s >>= 1;
}
int findtree(int l,int r) {
	int ans = 0x3f3f3f3f; if(l > r) return ans;
	for(int s=M+l-1,t=M+r+1;(s>>1) ^ (t>>1); s >>= 1,t >>= 1) {
		if(!(s&1)) ans = min(ans,tre[s^1]);
		if(t & 1) ans = min(ans,tre[t^1]);
	}return ans;
}
int hd[MAXN],nx[MAXN<<1],v[MAXN<<1],cne,ind[MAXN];
void ins(int x,int y) {
	nx[++ cne] = hd[x]; v[cne] = y; hd[x] = cne; ind[y] ++;
}
int as[MAXN];
int main() {
	n = read(); m = read();
	for(int i = 1;i <= n;i ++) {
		a[i] = read();
		ad[a[i]] = i;
	}
	maketree(n);
	for(int i = n;i > 0;i --) {
		int x = findtree(ad[i]+1,min(ad[i]+m-1,n));
		if(x <= n) ins(ad[x],ad[i]);
		x = findtree(max(1,ad[i]-m+1),ad[i]-1);
		if(x <= n) ins(ad[x],ad[i]);
		addtree(ad[i],i);
	}
	priority_queue<int> b;
	for(int i = 1;i <= n;i ++) {
		if(ind[i] == 0) b.push(i);
	}
	int cnt = 0;
	while(!b.empty()) {
		int t = b.top();b.pop();
		as[t] = n - (cnt ++);
		for(int i = hd[t];i;i = nx[i]) {
			ind[v[i]] --;
			if(ind[v[i]] == 0) b.push(v[i]);
		}
	}
	for(int i = 1;i <= n;i ++) AIput(as[i],'\n');
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值