CF1967D Long Way to be Non-decreasing

CF1967D Long Way to be Non-decreasing

给定长为 \(n\) 的序列 \(a_i\),值域为 \([1,m]\)。再给出 \(m\) 个变换 \(x \to b_x\),你可以对每个位置执行若干次变换,要求操作后 \(a_1 \le a_2 \le \cdots \le a_n\)。

最小化每个位置上最大的操作次数并输出,或报告无解。

多测,\(\sum n,\sum m \le 10^6\),4 秒,500 MB。

这个题本质 *1800。但是我模拟赛场上一直在想锤子数据结构,于是爆了。

考虑二分答案 \(k\),判定每个位置的操作次数 \(\le k\)。

贪心地,已知 \(a_i\) 确定 \(a_{i+1}\) 时,\(a_{i+1}\) 应取到能取到的最小值。直接扫值域 \(x=a_i,a_i+1,\cdots,m\),检查 \(a_{i+1} \leadsto x\) 是否合法。考虑 \(x\) 单调不降,复杂度可以均摊到 \(O(m)\)。

连边 \(x \to b_x\) 形成内向基环树森林,合法条件即为两点可达,且距离 \(\le k\)。对环和树分开处理,简单讨论即可。

总复杂度 \(O(n+m \log V)\)。

const int N = 1e6 + 5, inf = 1e9;
int n, m, a[N], to[N];
vector<int> e[N];

bool ont[N], vis[N];
int cnt, dn, deg[N], dep[N], rot[N], bel[N], pos[N], len[N], dfn[N], siz[N];

int dis(int u, int v) {
    if (u == v) return 0;
    if (ont[u] && ont[v]) {
        if (rot[u] != rot[v]) return inf;
        if (dfn[v] <= dfn[u] && dfn[u] < dfn[v] + siz[v]) return dep[u] - dep[v];
        return inf;
    }
    if (ont[u] ^ ont[v]) {
        if (ont[v]) return inf; 
        if (bel[v] != bel[rot[u]]) return inf;
        int l = len[bel[v]], d = pos[v] - pos[rot[u]];
        return dep[u] + (d >= 0 ? d : l + d);
    }
    if (bel[u] != bel[v]) return inf;
    int l = len[bel[u]], d = pos[v] - pos[u];
    return (d >= 0 ? d : l + d);
}

void prework() {
    cnt = 0, dn = 0, fill(bel + 1, bel + m + 1, 0);
    fill(ont + 1, ont + m + 1, 0), fill(vis + 1, vis + m + 1, 0);
    fill(deg + 1, deg + m + 1, 0), fill(dep + 1, dep + m + 1, 0);
    for (int i = 1; i <= m; i++) e[i].clear();

    for (int i = 1; i <= m; i++) deg[to[i]]++;
    queue<int> Q;
    for (int i = 1; i <= m; i++) if (deg[i] == 0) Q.emplace(i);
    while (!Q.empty()) {
        int u = Q.front(), v = to[u];
        Q.pop(), ont[u] = true;
        if (--deg[v] == 0) Q.emplace(v);
    }
    for (int i = 1; i <= m; i++) {
        if (!ont[i] && !ont[to[i]]) continue;
        e[to[i]].pb(i);
    }
    for (int i = 1; i <= m; i++) if (!ont[i]) { 
        auto dfs = [&](auto&& dfs, int u) -> void {
            rot[u] = i, dfn[u] = ++dn, siz[u] = 1;
            for (int v : e[u]) 
                dep[v] = dep[u] + 1, dfs(dfs, v), siz[u] += siz[v];
        };
        dfs(dfs, i);
    }
    for (int i = 1; i <= m; i++) if (!ont[i] && !bel[i]) {
        vector<int> q = {i}; bel[i] = ++cnt;
        for (int u = to[i]; u != i; u = to[u]) q.pb(u), bel[u] = cnt;
        len[cnt] = q.size();
        for (int k = 0; k < len[cnt]; k++) pos[q[k]] = k; 
    }
}

bool check(int k) {
    int x = 1;
    for (int i = 1; i <= n; i++) {
        while (x <= m && dis(a[i], x) > k) x++;
        if (x > m) return false;
    }
    return true;
}

void _main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= m; i++) cin >> to[i];
    prework();
    int l = 0, r = m, res = -1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) res = mid, r = mid - 1;
        else l = mid + 1;
    }
    cout << res << '\n';
} 
posted @ 2026-10-20 16:31  stripe_python  阅读(6)  评论(0)    收藏  举报