题解:CF1967D Long Way to be Non-decreasing
节选自:图论做题记录(三)(2025.5.24 - 2025.31)
首先,如果 \(x\) 次操作可以将 \(a\) 序列变成单调不降的,那么第 \(x + 1\) 次时,我们可以把序列中一个 \(a\) 赋值成它本身,此时序列仍是单调不降的(有点废话,但是是有用的废话),这说明这道题目具有单调性,我们可以先二分答案,那么原问题就变成了判断进行 \(x\) 次操作,能否将 \(a\) 序列变成单调不降的。
此时我们就有了一个贪心的想法,对于序列的第 \(i\) 个位置,它最终变成的这个数一定大于第 \(i - 1\) 个位置,且是最小的,这样才能让后面能变的数字变多。当无论如何进行这 \(x\) 次操作,\(a_i\) 都比 \(a_{i - 1}\) 小的时候,那么就无解了。
不过这样直接判断 \(a_i\) 进行 \(x\) 次操作能变成哪些数肯定会超时,我们考虑优化它。由于有连续将一个数变成另一个数的操作,那么我们考虑将复值的过程建成图,从每个 \(i\) 向 \(b_i\) 连边,此时就构成了一棵基环树森林,一个数变成另一个数需要的操作数,也就变成了基环树上两点距离。
于是我们正着扫一遍整个序列,枚举 \(now\) 为当前可以填的最小的数,如果 \(a_i\) 到 \(now\) 的距离小于 \(x\),那么 \(now\) 就不变,否则 \(now\) 就不断增加直到 \(a_i\) 到 \(now\) 的距离小于 \(x\),如果 \(now\) 已经大于 \(m\),那就无解了,否则就有解。由于 \(now\) 是单调不降的,因此二分答案 check
一次是 \(O(n)\) 的,于是我们用 \(O(T n \log n)\) 的时间复杂度解决了这个问题。
关于如何在有向基环树上求两点间的距离,见图论学习笔记(五):特殊图 / 基环树 / 基环树上两点距离 / 有向图。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define MAXSIZE (1 << 20)
char buf[MAXSIZE], *p1, *p2;
char pbuf[MAXSIZE], *pp;
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin), p1 == p2) ? EOF : *p1++)
#define pc putchar
inline int read(){
int x = 0;
char ch = gc();
while(!isdigit(ch))
ch = gc();
while(isdigit(ch)){
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = gc();
}
return x;
}
inline void write(int x){
if(x < 0){
pc('-');
x = -x;
}
if(x > 9)
write(x / 10);
pc(x % 10 + '0');
}
const int N = 1e6 + 9, INF = 0x3f3f3f3f;
struct Edge{
int v, nex;
} e[N << 1];
int head[N], ecnt;
void addEdge(int u, int v){
e[++ecnt] = Edge{v, head[u]};
head[u] = ecnt;
}
int a[N], b[N], fa[N], del[N], L[N], R[N], dep[N], dfncnt, n, m, T;
int find(int x){
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void dfs(int u){
L[u] = ++dfncnt;
for(int i = head[u]; i; i = e[i].nex){
int v = e[i].v;
dep[v] = dep[u] + 1;
dfs(v);
}
R[u] = dfncnt;
}
bool pd(int v, int u){
return L[u] <= L[v] && R[u] >= L[v];
}
int ask(int u, int v){
if(find(u) != find(v))
return INF;
if(pd(u, v))
return dep[u] - dep[v];
if(pd(b[del[find(u)]], v))
return dep[u] + 1 + dep[b[del[find(u)]]] - dep[v];
return INF;
}
int check(int mid) {
int now = 1;
for(int i = 1; i <= n; ++ i) {
while(now <= m && ask(a[i], now) > mid)
now++;
if(now > m)
return 0;
}
return 1;
}
void init(){
for(int i = 1; i <= m; i++)
head[i] = del[i] = dep[i] = 0;
dfncnt = ecnt = 0;
}
int main(){
T = read();
while(T--){
init();
n = read();
m = read();
for(int i = 1; i <= n; i++)
a[i] = read();
for(int i = 1; i <= m; i++){
fa[i] = i;
b[i] = read();
}
for(int i = 1; i <= m; i++){
int x = find(i), y = find(b[i]);
if(x != y){
fa[x] = y;
addEdge(b[i], i);
} else
del[x] = i;
}
for(int i = 1; i <= m; i++){
if(fa[i] == i){
dfncnt = 0;
dfs(del[i]);
}
}
int l = 0, r = m, ans = - 1;
while(l <= r) {
int mid = ((l + r) >> 1);
if(check(mid)){
ans = mid;
r = mid - 1;
}
else
l = mid + 1;
}
write(ans);
pc('\n');
}
return 0;
}
本文来自博客园,作者:Orange_new,转载请注明原文链接:https://www.cnblogs.com/JPGOJCZX/p/18904065