1157 Round 555 DIV3 :E. Minimum Array(贪心 + 并查集优化 | multiset)

题目大意:
有两个序列a,b,a序列和b序列的每个元素 , (ai + bi) % n 的值可以构成ci,题目希望通过调整b序列的顺序,来使得C序列字典序最小。

解法:先扫一遍b序列,可以得到数值的个数,对于ai的每个元素,从左往右起,若数值:(n - a[i]) % n 的个数还有剩,则bi这个位置换成这个数值,否则,贪心的往上找,找(n - a[i] + 1) % n 是否存在,有就放,没有就继续往下,因为元素个数都是n,循环一圈每个数字总能找到要放的数字。但找数字的过程是一个可优化的过程,中间如果空了很大一段我们可以跳着找,这种跳着找的优化就叫并查集。

具体来说,每个数值有个指针数组,若该数值存在(b序列中有这个数值),则指针数组指向该数值,否则指针数组指向下一个数字,这样就搞成了一个链表,每次我们沿着链表去找就可以。而并查集的路径压缩,可以优化这个链表,因为链表正是并查集退化的情况,这也是并查集优化的一种常见形式(终于补上了对并查集的理解)。

至于multiset,其实就是暴力的做法,每用掉一个数字,就删掉一个数字,因为找不到数字的时候我们是递增的往上找,所以可以直接在multiset上二分查找我们可以放的最优的数值,而如果在右边区间查找不到,那么就在左边区间找个最小的,看别人都是跑400+ ms。并查集优化只跑140ms。(并查集优化的复杂度优于log(n),是必备的优化技能啊)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int vis[maxn];
int a[maxn],b[maxn],c[maxn];
int n;
int p[maxn];
int find(int x) {
	int t = x;
	while(t != p[t])
		t = p[t];
	int fa;
	while(x != t) {
		fa = p[x];
		p[x] = t;
		x = fa;
	}
	return t;
}
int main() {
	scanf("%d",&n);
	for(int i = 1; i <= n; i++) {
		scanf("%d",&a[i]);
	}
	for(int i = 1; i <= n; i++) {
		scanf("%d",&b[i]);
		vis[b[i]]++;
	}
	for(int i = 0; i < n; i++) {
		p[i] = i;
	}
	for(int i = 0; i < n; i++) 
		p[i] = vis[i] ? i : (i + 1) % n;
	for(int i = 1; i <= n; i++) {
		int v = a[i];
		int u = (n - a[i]) % n;
		int num = find(u);
		c[i] = (a[i] + num) % n;
		vis[num]--;
		if(!vis[num]) p[num] = (num + 1) % n;
	}
	for(int i = 1; i <= n; i++) {
		printf("%d ",c[i]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/89606974