zoukankan      html  css  js  c++  java
  • 8.30 广泛交换意见

    题意

    给一个长度为(N)的排列(P)与一个正整数(K),可以进行如下操作:

    对于两个满足(|i-j|geq K)(|P_i-P_j|=1)的下标(i)(j),交换(P_i)(P_j)

    我们的目的是要求操作后的排列字典序最小


    解法

    首先,这两个条件都不好判断,直接做是不太好搞的

    考虑把这个数组映射一下(这好像是对于排列的一个常见套路?)

    (pos[P_i]=i),即元素(P_i)的位置为(i)

    我们对(pos)数组进行操作,操作就转化为了:

    可以交换两个相邻元素,并且相邻元素的差的绝对值(geq K)

    我们发现,对于两个元素(i,j,i<j),如果(|i-j|<K),那么它们的相对位置永远不会改变

    也就是说无论进行多少次交换,如何交换,(i)一定会在(j)之前

    这就启发我们用拓扑排序进行求解

    但是我们应按照一种什么样的规则选择求出的拓扑序,使得原排列的字典序最小呢?

    网上有许多题解都认为,原序列的字典序最小等价于求逆序列的字典序最小

    但实际上这个想法是错误的,可以举一组反例来说明

    • (rev{2.3.1} = {3.1.2})

    • (rev{3.1.2} = {2.3.1})

    我们可以发现,第一个排列的字典序比第二个大,但其逆序列的字典序却比第二个小

    实际上,真正正确的结论是原序列的字典序最小,等价于其逆序列的反序列的字典序最大

    那么接下来的任务就是建图了

    为了避免重复连边,我们规定一个元素仅能向其后面的元素连边

    考虑连边的意义,若存在一条有向边(E(u,v)),说明(u,v)之间的相对顺序已经确定即(v)(u)之后

    对于一个元素(x),它会向值域为((x-K,x)cup (x,x+K))的元素连边,并且要求这些元素的初始位置在(x)之后

    但是这样暴力连边,边的数量是(O(N^2))级别的

    我们可以发现,相对顺序是有传递关系的,比如说对于三条边(A ightarrow B, B ightarrow C,A ightarrow C),第三条边完全可以被前两条边替代,那么它就是无效的

    为了去掉这些无效的边,我们规定(x)只向值域为((x-K,x))((x,x+K))中的位置在(x)之后且最小的元素连边

    这是因为我们可以发现由于这两个集合的大小均不超过(K),那么它们内部一定是互有连边的

    那么我们只需要连一个位置最靠前的元素,就可以根据这个元素扩张到整个集合了

    由于是求逆序列的反序列最大,我们需要建反图,并用大根堆求字典序最大的序列

    找到这个元素可以用线段树实现,由于合法的元素一定在当前元素之后,所以倒序加点即可


    代码

    #include <queue>
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    
    using namespace std;
    
    const int N = 5e5 + 10;
    
    int read();
    
    int n, k;
    int p[N], q[N];
    
    int cap;
    int head[N], deg[N], to[N << 1], nxt[N << 1];
    
    priority_queue<int> que;
    
    inline int min(int x, int y) {
    	return x < y ? x : y;	
    }
    
    inline int max(int x, int y) {
    	return x > y ? x : y;	
    }
    
    struct SegTree {
    	
    	int val[N << 2];
    	
    	void init() {
    		memset(val, 0x3f, sizeof val); 
    	}
    	void change(int x, int l, int r, int k, int v) {
    		if (l == r)	return val[x] = v, void();
    		int mid = l + r >> 1;
    		if (k <= mid)	
    			change(x << 1, l, mid, k, v);
    		else
    			change(x << 1 | 1, mid + 1, r, k, v);
    		val[x] = min(val[x << 1],  val[x << 1 | 1]); 
    	}
    	int query(int x, int l, int r, int ls, int rs) {
    		if (ls <= l && r <= rs)	return val[x];
    		int mid = l + r >> 1, res = 0x3f3f3f3f;
    		if (ls <= mid)	
    			res = min(res, query(x << 1, l, mid, ls, rs));
    		if (rs > mid)
    			res = min(res, query(x << 1 | 1, mid + 1, r, ls, rs));
    		return res;	
    	}
    } tr;
    
    inline void add(int x, int y) {
    	to[++cap] = y, nxt[cap] = head[x], head[x] = cap;	
    	++deg[y];
    }
    
    int main() {
    	
    	n = read(), k = read();
    	for (int i = 1; i <= n; ++i)	p[i] = read();
    	for (int i = 1; i <= n; ++i)	q[p[i]] = i;
    	
    	tr.init();
    	for (int i = n; i >= 1; --i) {
    		int x = tr.query(1, 1, n, q[i], min(q[i] + k - 1, n));
    		if (x <= n)
    			add(q[x], q[i]);
    		int y = tr.query(1, 1, n, max(q[i] - k + 1, 1), q[i]);
    		if (y <= n)
    			add(q[y], q[i]);
    		tr.change(1, 1, n, q[i], i);
    	}
    	
    	for (int i = 1; i <= n; ++i)
    		if (!deg[i])	que.push(i);
    	for (int i = n; i >= 1; --i) {
    		int u = que.top(); que.pop();
    		p[i] = u;
    		for (int i = head[u]; i; i = nxt[i])
    			if (!--deg[to[i]])	que.push(to[i]);
    	}
    	
    	for (int i = 1; i <= n; ++i)	q[p[i]] = i;
    	for (int i = 1; i <= n; ++i)	printf("%d
    ", q[i]);
    	
    	return 0;
    }
    
    int read() {
    	int x = 0, c = getchar();
    	while (!isdigit(c))	c = getchar();
    	while (isdigit(c))	x = x * 10 + c - 48, c = getchar();
    	return x;	
    }
    
  • 相关阅读:
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571 按顺序等距排列平行直线
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571GS标记子实体
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571获取和修改摘要信息
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571长事务
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571按表走呵呵!(延时死循环)
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571修改多段线的凸度
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571临时反应器的简单应用
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571自创实体类从AcDbCurve派生,实现一些类方法并动态绘制
    2021年1月1日 AutoCAD.Net/C#.Net QQ群:193522571多段线动态切割闭合区域
    动态块
  • 原文地址:https://www.cnblogs.com/VeniVidiVici/p/11437438.html
Copyright © 2011-2022 走看看