其他分享
首页 > 其他分享> > CF1685D Permutation Weight [贪心,构造]

CF1685D Permutation Weight [贪心,构造]

作者:互联网

传送门

思路

令 \(p' = p^{-1}\),即 \(p'_{p_i} = i\),则原题等价于最小化 \(\sum |p'_{q_i} - q_{i+1}|\)。显然,当所有 \(i\) 都满足 \(p'_{q_i} = q_{i+1}\) 时原式取最小值,但这时 \(q\) 不一定是一个排列。容易发现,排列 \(q\) 给出了一个在 \(p'\) 形成的图上遍历的顺序,考虑 \(p'\) 的置换分解,对每个环染色,那么为了让更多 \(i\) 满足 \(p'_{q_i} = q_{i+1}\),\(q\) 应当尽可能在同色的环上跳转。设有 \(k\) 个这样的环。

考虑连边 \(p'_{q_i} \to q_{i+1}\),则每个点的入度和出度恰为 \(1\),因此它们形成一些环。注意到大小为 \(m\) 的环对答案的贡献至少为 \(2(m-1)\),取到这个下界当且仅当环上的点编号连续且是单峰的。又因为 \(q\) 需要能够遍历整张图,这还要求将每个置换环缩点后所有颜色必须连通。大小为 \(m\) 的环至多能减少 \(m-1\) 个颜色连通块,而总共需要减少 \(k-1\) 个颜色连通块,则 \(\sum (m-1) \geq k-1\),故答案有下界 \(2(k-1)\)。

事实上这个下界可以达到,当且仅当这张图合法,且每个环对答案的贡献都到达了下界,并且这些环不会多次合并某两个颜色。容易发现,合法的环会覆盖 \(1 \sim n\) 的一个完整的区间。

对于 Easy Version,只需枚举 \(i\),若 \(i\) 和 \(i+1\) 颜色不同且它们未被合并就将他们合并,最后把区间还原成环即可。

对于 Hard Version,按照字典序添加每一条边,我们的目标是判断目前加入的边能否构造出到达下界的解。显然,已经连出的边只可能形成环,单增的链或单减的链。容易得到合法的条件:

时间复杂度 \(O(n^3)\)。

Code
/*
也许所有的执念 就像四季的更迭
没有因缘 不需致歉
是否拥抱着告别 就更能读懂人间
还是感慨 更多一点
*/
#include <bits/stdc++.h>
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define int long long
#define mem(x, v, n) memset(x, v, sizeof(int) * (n))
#define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n))
#define lob lower_bound
#define upb upper_bound
using namespace std;

inline int read() {
	int x = 0, w = 1;char ch = getchar();
	while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); }
	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * w;
}

inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }

const int MN = 5e2 + 5;
const int Mod = 1e9 + 7;

inline void Pls(int &x, int y) { x += y; if (x >= Mod) x -= Mod; }
inline void Dec(int &x, int y) { x -= y; if (x < 0) x += Mod; }

inline int qPow(int a, int b = Mod - 2, int ret = 1) {
    while (b) {
        if (b & 1) ret = ret * a % Mod;
        a = a * a % Mod, b >>= 1;
    }
    return ret;
}

// #define dbg

int N, p[MN], mp[MN], col[MN], cnt, in[MN], out[MN], ans[MN], L[MN], R[MN], vis[MN];
vector <int> G[MN];

inline void DFS(int u) {
    if (vis[u]) return;
    vis[u] = 1;
    for (int v : G[u]) DFS(v);
}
inline int Chk() {
    for (int i = 1; i <= N; i++) if (L[i] + (in[i] == i) > 1 || R[i] + (in[i] == i) > 1) return 0;
    for (int i = 1; i <= N; i++) if (!in[i] && !out[i] && L[i] && R[i]) return 0;
    for (int i = 1; i <= cnt; i++) G[i].clear(), vis[i] = 0;
    int c = 0, bc = 0;
    for (int i = 1; i < N; i++) if (L[i + 1] || R[i]) c++, G[col[i]].pb(col[i + 1]), G[col[i + 1]].pb(col[i]);
    for (int i = 1; i <= cnt; i++) if (!vis[i]) bc++, DFS(i);
    if (c + bc > cnt) return 0;
    for (int i = 1; i <= cnt; i++) G[i].clear(), vis[i] = 0;
    c = 0;
    for (int i = 1; i < N; i++) if (!(in[i] && out[i] && (in[i] <= i && out[i] <= i)) && !(in[i + 1] && out[i + 1] && (in[i + 1] >= i + 1 && out[i + 1] >= i + 1))) {
        G[col[i]].pb(col[i + 1]);
        G[col[i + 1]].pb(col[i]);
    }
    for (int i = 1; i <= cnt; i++) if (!vis[i]) c++, DFS(i);
    return c > 1 ? 0 : 1;
}
inline void Add(int u, int v) {
    out[u] = v, in[v] = u;
    if (v < u) for (int i = u; i > v; i--) L[i]++;
    else for (int i = u; i < v; i++) R[i]++;
}
inline void Del(int u, int v) {
    out[u] = in[v] = 0;
    if (v < u) for (int i = u; i > v; i--) L[i]--;
    else for (int i = u; i < v; i++) R[i]--;
}
inline void Work() {
    N = read();
    for (int i = 1; i <= N; i++) p[i] = read(), mp[p[i]] = i;
    cnt = 0;
    for (int i = 1; i <= N; i++) col[i] = in[i] = out[i] = L[i] = R[i] = 0;
    for (int i = 1; i <= N; i++) if (!col[i]) {
        col[i] = ++cnt;
        int x = mp[i];
        while (x != i) col[x] = cnt, x = mp[x];
    }
    ans[1] = 1;
    for (int i = 2; i <= N; i++) {
        int u = ans[i - 1];
        for (int j = 1; j <= N; j++) {
            int v = p[j];
            if (in[v]) continue;
            Add(u, v);
            if (Chk()) { 
                ans[i] = j; break; 
            }
            Del(u, v);
        }
    }
    for (int i = 1; i <= N; i++) printf("%lld%c", ans[i], " \n"[i == N]);
}

signed main(void) {
    int T = read();
    while (T--) Work();
    return 0;   
}

标签:ch,Weight,int,MN,CF1685D,Permutation,inline,Mod,define
来源: https://www.cnblogs.com/came11ia/p/16496291.html