Long Way to be Non-decreasing 题解
令 \(b[i][j]\) 为用 \(b[i]\) 转移 \(j\) 次。
容易想到这题是二分答案题,一个贪心的思想,对于每一个 \(a_i\) ,在 \(b[a[i]][0]\sim b[a[i]][mid]\) 这些点中大于等于 \(a_{i-1}\) 的最小的值即可,时间复杂度应该是 \(O(n^2\log n)\)。
然而,这一题可以转换成图论题,对 \(i\rightarrow b_i\) 这样的图是一个基环森林,然后我们需要找到 \(i \rightarrow b[i][mid]\) 的这一段区间的大于等于 \(a_{i-1}\) 的最小的值即可,这个可以使用数据结构维护,这题就好了。
当然这还是太难写了,而且这个是一个 \(O(n\log^2n)\) 的算法,可能会被卡掉,还是容易被数据结构学傻了。
那这题有一个非常的有趣的性质——单调性。可以将 check 的时间复杂度优化到 \(O(n)\) 。具体这么做呢?我们没必要直接找到 \(a_{i-1}\) ,而是枚举的找到 \(a_{i-1}\) ,看下这个点是否能达到,就是求一下基环树两点的距离是否小于 \(mid\) ,这个结论非常的显然,但是在考场上不太容易想到。
对于基环树的两点的距离,这是一个内向基环树,对于 \(dist(x,y)\) 我们需要分类讨论:
- 这两个点不在一个基环树上,距离为 \(\inf\)。
- \(x,y\) 不在一个环上的一点的祖先里,且不都在环上,\(\inf\)
- \(x,y\) 在环上直接求环上距离。
- \(x,y\) 在同一个子树内,但 \(y\) 不是 \(x\) 的祖先,\(\inf\)。
- \(x,y\) 在同一个子树内,且 $ y$ 是 \(x\) 的祖先,是树上两点的距离。
- \(x\) 在某个子树内,而 \(y\) 在基环树内,求 \(x\) 的根到 \(y\) 的距离加上 \(x\) 的深度。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5, inf = 1e9;
int n, m, T;
int a[N], b[N], p[N], t;
int flag[N];
int bel[N], idx[N], cnt, siz[N], rt[N], dep[N], L[N], R[N];
bool vis[N];
//记录在环上的点
vector<int> e[N], g[N];
int dist(int x, int y) {
if (bel[rt[x]] != bel[rt[y]])
return inf;
if (rt[x] != rt[y]) {
if (flag[y])
return inf;
return dep[x] + (idx[y] - idx[rt[x]] + siz[bel[y]]) % siz[bel[y]];
} //不在同一个环点上
else {
if (L[y] <= L[x] && R[x] <= R[y])
return dep[x] - dep[y];
else
return inf;
}
} // x->y 的距离
bool check(int mid) {
for (int i = 1, now = 1; i <= n; i++) {
while (now <= m && dist(a[i], now) > mid) now++;
if (now > m)
return false;
}
return true;
}
void redfs(int u, int root) {
L[u] = ++cnt;
rt[u] = root;
for (int v : g[u])
if (flag[v])
dep[v] = dep[u] + 1, redfs(v, root);
R[u] = cnt;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", a + i);
for (int i = 1; i <= m; i++)
e[i].clear(), g[i].clear(), p[i] = vis[i] = flag[i] = rt[i] = dep[i] = siz[i] = 0;
cnt = 0, t = 0;
for (int i = 1; i <= m; i++)
scanf("%d", b + i), e[i].push_back(b[i]), p[b[i]]++, g[b[i]].push_back(i);
queue<int> q;
for (int i = 1; i <= m; i++)
if (!p[i])
q.push(i), flag[i] = 1;
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i : e[x])
if (!--p[i])
q.push(i), flag[i] = 1;
}
for (int i = 1; i <= m; i++) {
if (!flag[i]) {
int now = i;
if (!vis[now])
++t;
while (!vis[now]) {
vis[now] = 1;
bel[now] = t;
idx[now] = ++cnt;
siz[t]++;
now = b[now];
}
}
}
cnt = 0;
for (int i = 1; i <= m; i++)
if (!flag[i])
redfs(i, i);
int l = 0, r = m, ans = -1;
// cout<<dist(6,4)<<endl;
// cout<<check(0)<<endl;
// return 0;
while (l <= r) {
int mid = l + r >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else
l = mid + 1;
}
cout << ans << endl;
}
return 0;
}

浙公网安备 33010602011771号