• CF1685D2 Permutation Weight 【构造,贪心】


    为了方便,规定下标在循环意义下。

    给定长为 \(n\) 的排列 \(p\),求长为 \(n\) 的排列 \(q\) 使得 \(\sum_{i=1}^n|q_i-p_{q_{i+1}}|\) 最小,若相同则求字典序最小的。

    \(2\le n\le 200\)\(\sum n\le 400\)


    考虑 \(q_i\to p_{q_{i+1}}\) 形成一堆环,一个环 \((b_1,\cdots,b_m)\) 贡献 \(\sum_{i=1}^m|b_i-b_{i+1}|\)。当 \(b_i\) 是权值连续的单峰数列时取到最小值 \(2(m-1)\)

    考虑 \(p\) 的每个环,将每个环缩成一个点,而 \(q_i\)\(p_{q_i}\) 在同个环里,所以形成一个大环,从而要求整个图连通,考虑其生成树,每个长为 \(m\) 的环只能选 \(m-1\) 条边,从而 \(\sum(m-1)\ge k-1\),即答案 \(\ge 2(k-1)\)

    构造说明可以取到等号:顺序枚举 \(x\in[1,n)\),若 \(x\)\(x+1\) 不在同一个环,就用 \(q_i\to p_{q_{i+1}}\) 连成一个环,这就做完了 Easy Version,复杂度 \(\mathcal O(n\cdot\alpha(n))\)

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 202;
    int T, n, q[N], fa[N];
    int getf(int x){return x == fa[x] ? x : fa[x] = getf(fa[x]);}
    void comb(int x, int y){x = getf(x); y = getf(y); if(x != y) fa[x] = y;}
    void solve(){
    	cin >> n;
    	for(int i = 1;i <= n;++ i) fa[i] = i;
    	for(int i = 1, x;i <= n;++ i){cin >> x; q[x] = i; comb(i, x);}
    	for(int i = 1;i < n;++ i) if(getf(i) != getf(i + 1)){swap(q[i], q[i + 1]); comb(i, i + 1);}
    	for(int i = 1, x = 1;i <= n;++ i, x = q[x]) printf("%d%c", x, " \n"[i == n]);
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin >> T; while(T --) solve();
    }
    

    现在看看 Hard Version 咋搞,事实证明上述条件也是充分的,然后就按位贪心判断:首先 \(q_1=1\),问题转化为判断前缀 \(q_1,\cdots,q_l\)\(l-1\) 条边 \(q_i\to p_{q_{i+1}}\) 能否满足要求,这就有些复杂了,需要仔细审视条件:

    • 首先是单峰的条件:所有 \(q_i<p_{q_{i+1}}\) 的区间 \((q_i,p_{q_{i+1}})\) 不相交,所有 \(q_i>p_{q_{i+1}}\) 的区间 \((p_{q_{i+1}},q_i)\) 不相交;
    • 然后是权值连续的条件:对于 \(x\in[1,n)\),若 \((x,x+1)\) 被两个区间包含,则要求 \(x\) 是区间端点。
    • 然后是生成树的条件,首先是无环的要求:对于 \(x\in[1,n)\),若 \((x,x+1)\) 被某个区间包含,则对 \(x\)\(x+1\) 的所在环连边,要求无环;
    • 最后是连通的要求:对于 \(x\in[1,n)\),若不存在 \(q_i=p_{q_{i+1}}=x\)\(x+1\),且 \(x\) 是至多一个区间的右端点,且 \(x+1\) 是至多是一个区间的左端点,则对 \(x\)\(x+1\) 的所在环连边,要求连通。

    直接模拟即可,时间复杂度 \(\mathcal O(n^3\cdot\alpha(n))\)

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 202;
    int n, k, p[N], fa[N], id[N], ans[N];
    bool vis[N];
    int getf(int x){return x == fa[x] ? x : fa[x] = getf(fa[x]);}
    int comb(int x, int y){x = getf(x); y = getf(y); if(x != y){fa[x] = y; return 0;} return 1;}
    bool v[3][N];
    int cnt[2][N];
    bool chk(int l){
    	for(int i = 0;i < 3;++ i) memset(v[i], 0, n + 1);
    	for(int i = 0;i < 2;++ i) memset(cnt[i], 0, (n + 1) << 2);
    	for(int i = 1;i < l;++ i){
    		int x = ans[i], y = p[ans[i + 1]];
    		if(x == y){v[2][x] = 1; continue;}
    		bool f = x > y; if(f) swap(x, y);
    		++ cnt[0][x]; ++ cnt[1][y];
    		for(int j = x;j < y;++ j){
    			if(v[f][j]) return 0;
    			v[f][j] = 1;
    		}
    	}
    	for(int i = 1;i < n;++ i) if(v[0][i] && v[1][i] && !cnt[0][i] && !cnt[1][i]) return 0;
    	iota(fa, fa + k + 1, 0);
    	for(int i = 1;i < n;++ i) if((v[0][i] || v[1][i]) && comb(id[i], id[i + 1])) return 0;
    	iota(fa, fa + k + 1, 0);
    	for(int i = 1;i < n;++ i) if(!v[2][i] && !v[2][i + 1] && cnt[0][i + 1] < 2 && cnt[1][i] < 2) comb(id[i], id[i + 1]);
    	for(int i = 2;i <= k;++ i) if(getf(1) != getf(i)) return 0;
    	return 1;
    }
    void solve(){
    	cin >> n; k = 0;
    	memset(vis, 0, n + 1);
    	memset(id, 0, (n + 1) << 2);
    	for(int i = 1;i <= n;++ i) cin >> p[i];
    	for(int i = 1;i <= n;++ i) if(!id[i]){
    		id[i] = ++ k;
    		for(int j = p[i];j != i;j = p[j]) id[j] = k;
    	}
    	ans[1] = 1; putchar('1');
    	for(int i = 2;i < n;++ i){
    		for(ans[i] = 2;ans[i] <= n;++ ans[i]) if(!vis[ans[i]] && chk(i)) break;
    		vis[ans[i]] = 1; printf(" %d", ans[i]);
    	}
    	for(ans[n] = 2;ans[n] <= n;++ ans[n]) if(!vis[ans[n]]) break;
    	printf(" %d\n", ans[n]);
    }
    int main(){
    	ios::sync_with_stdio(0);
    	int T; cin >> T; while(T --) solve();
    }
    
  • 相关阅读:
    Mysql高手系列
    Mysql高手系列
    Mysql高手系列
    Mysql高手系列
    Mysql高手系列
    Mysql高手系列
    Mysql高手系列
    Mysql高手系列
    Mysql高手系列
    Mysql高手系列
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/16348458.html
Copyright © 2020-2023  润新知