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';
}

浙公网安备 33010602011771号