Long Way to be Non-decreasing 题解
前言
题意简述
yzh 喜欢单调不降序列。
她有一个序列 \(a\),最初为 \(a_1, \ldots, a_n\),其中每个元素都在 \([1, m]\) 内。
她希望使序列变得单调不降,为此,她有一个序列 \(b_1, \ldots, b_m\),每个元素也在 \([1, m]\) 内。她可以进行若干次操作,一次操作定义为:
- 选择一个集合 \(S \subseteq \lbrace 1, 2, \ldots, n \rbrace\)。
- \(\forall i \in S\),\(a_i \leftarrow b_{a_i}\)。
yzh 想知道至少需要多少次操作可以使 \(a\) 变单调不降。如果不可能,输出 \(-1\)。
多组数据,\(\sum n,\sum m \leq 10^6\)。
题目分析
首先能想到,每次选择一个集合操作是唬人的,不妨按照每一个位置来算。发现这样操作的总步数是所有位置中操作次数最多的那一个,最大值最小,很容易想到二分。那么如何 check 呢?
设当前判断能否在 \(mid\) 次操作内使 \(a\) 变得单调不降。有一个贪心的想法,从左向右考虑,设 \(a_i\) 操作 \(mid\) 次所能到达的集合(包括一次也不操作,即 \(a_i\) 本身)为 \(S\),那么令 \(a_i \leftarrow \min \lbrace x \mid x \in S \wedge x \geq a_{i - 1} \rbrace\),当然,\(a_1\) 没有 \(a_1 \geq a_0\) 的限制,可以直接当 \(a_0 = 0\)。什么时候无解呢?当某一位的 \(S = \varnothing\),即 \(a_i\) 没有任何一个可行解的时候无解。一个 naive 的想法就是每一位干 \(mid\) 次然后判断一下取最小值,显然超时,考虑优化。
考虑倒着考虑,枚举 \(a_i\) 能否在 \(mid\) 步之内变成 \(a_{i - 1}, a_{i - 1} + 1, \ldots, m\)。发现,当 \(a_i\) 取了一个值,那么 \(a_{i + 1}\) 就会从 \(a_i\) 开始考虑,这显然是单调的。所以考虑从左向右枚举的时候维护一个指针 \(j\),当 \(\operatorname{dist}(a_i, j) > mid\) 时,就让 \(j \leftarrow j + 1\),其中 \(\operatorname{dist}(xym, yzh)\) 表示 \(xym\) 变换到 \(yzh\) 的操作数。
考虑实现 \(\operatorname{dist}(xym, yzh)\)。很容易地连边 \(i \rightarrow b_i\),形成一个内向基环树森林。
- 当 \(xym\) 和 \(yzh\) 不在一棵基环树内时,\(\operatorname{dist}(xym, yzh) = \infty\)。
- 当 \(xym\) 和 \(yzh\) 在同一棵基环树内时:
- \(yzh\) 是 \(xym\) 的祖先,\(\operatorname{dist}(xym, yzh) = dpt_{xym} - dpt_{yzh}\)。
- \(yzh\) 是环上一点,设 \(xym\) 是环上 \(p\) 的子孙,\(\operatorname{dist}(xym, yzh) = dpt_{xym} - dpt_{p} + \operatorname{dis}(p, yzh)\),实现环上距离 \(\operatorname{dis}(a, b)\) 是 naive 的。
- 其他情况 \(\operatorname{dist}(xym, yzh) = \infty\)。
到此为止,我们已经能完成这道题了。但是,我要讲另一种更方便求得基环树森林中两点距离的方法,一下只考虑 \(xym\) 和 \(yzh\) 在同一棵基环树内。
考虑拆环成树,断开环上任意一条边 \(u \rightarrow b_u\),再以 \(u\) 为根,做一遍内向树上的深搜。考虑这时候计算 \(\operatorname{dist}(xym, yzh)\)。
- 若 \(xym \in \operatorname{subtree}(yzh)\),则 \(\operatorname{dist}(xym, yzh) = dpt_{xym} - dpt_{yzh}\),\(\operatorname{subtree}(yzh)\) 可以用 dfs 序实现。
- 若 \(\exists \operatorname {Path}(xym \rightarrow u \rightarrow b_u \rightarrow yzh)\),则 \(\operatorname{dist}(xym, yzh) = dpt_{xym} - dpt_{u} + 1 + dpt_{b_u} - dpt_{yzh}\)。
- 其他情况 \(\operatorname{dist}(xym, yzh) = \infty\)。
这显然是正确的,可以配合下图理解。

令 \(n\) 和 \(m\) 同阶,两种方法时间复杂度 \(\Theta(n (\alpha(n) + \log n))\),空间复杂度 \(\Theta(n)\)。
代码
实际连边的时候是连的外向树,这样才能做 dfs。挺快的,卡卡常洛谷 Rank1,下面略去了快读快写。
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std;
struct Graph{
struct node{
int to, nxt;
} edge[1000010];
int eid, head[1000010];
inline void add(int u, int v){
edge[++eid] = {v, head[u]};
head[u] = eid;
}
inline node & operator [] (const int x){
return edge[x];
}
} xym;
int n, m;
int val[1000010], trans[1000010];
int del[1000010], L[1000010], R[1000010], timer;
int dpt[1000010];
void dfs(int now){
L[now] = ++timer;
for (int i = xym.head[now], to; to = xym[i].to, i; i = xym[i].nxt) dpt[to] = dpt[now] + 1, dfs(to);
R[now] = timer;
}
int fa[1000010];
int get(int x){
return fa[x] == x ? x : fa[x] = get(fa[x]);
}
bool merge(int x, int y){
return x = get(x), y = get(y), x != y && (fa[x] = y, true);
}
inline bool insub(int v, int u){
// v 在不在 u 的子树里
return L[u] <= L[v] && L[v] <= R[u];
}
inline int dis(int x, int y){
if (get(x) != get(y)) return 0x3f3f3f3f;
if (insub(x, y)) return dpt[x] - dpt[y];
// x 在 y 的子树里,直接往上跳
if (insub(trans[del[get(x)]], y)) return dpt[x] + 1 + dpt[trans[del[get(x)]]] - dpt[y];
// 看看能不能跳过断掉的边
return 0x3f3f3f3f;
}
bool check(int k){
// j 即是维护的指针
for (int i = 1, j = 1; i <= n; ++i){
while (j <= m && dis(val[i], j) > k) ++j;
if (j > m) return false;
}
return true;
}
void solve(){
read(n), read(m), timer = 0, xym.eid = 0;
for (int i = 1; i <= n; ++i) read(val[i]);
for (int i = 1; i <= m; ++i) read(trans[i]), fa[i] = i, del[i] = 0, xym.head[i] = 0, dpt[i] = 0;
for (int i = 1; i <= m; ++i)
if (!merge(i, trans[i])) del[get(i)] = i;
// 如果找到了环,那么把 i -> trans[i] 这条边删除
else xym.add(trans[i], i);
// 否则建出内向树森林
for (int i = 1; i <= m; ++i) if (fa[i] == i) dfs(del[i]); // 跑 dfs 序
int l = 0, r = m, mid, ans = -1;
while (l <= r){
mid = (l + r) >> 1;
if (check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
// 简单二分
if (ans == -1) putchar('-'), putchar('1'), putchar('\n');
else write(ans), putchar('\n');
}
signed main(){
int t; read(t);
while (t--) solve();
return 0;
}
Long Way to be Non-decreasing 题解的更多相关文章
- UVA 10131题解
第一次写动态规划的代码,整了一天,终于AC. 题目: Question 1: Is Bigger Smarter? The Problem Some people think that the big ...
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解
题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...
- 2016ACM青岛区域赛题解
A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- poj1399 hoj1037 Direct Visibility 题解 (宽搜)
http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...
- 网络流n题 题解
学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...
- CF100965C题解..
求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...
随机推荐
- 微信支付or支付宝支付调用流程图
微信支付or支付宝支付调用流程图 支付宝小程序支付调用流程https://opendocs.alipay.com/mini/03l735 微信H5支付调用流程https://pay.weixin.qq ...
- redisTemplate缓存方法template code
import com.alibaba.fastjson.JSONObject; @Autowired private RedisTemplate redisTemplate; String PREFI ...
- kettle从入门到精通 第七十课 ETL之kettle kettle数据校验,脏数据清洗轻松拿捏
场景:输入在指定的错误(错误应涵盖数据类型不匹配的情况)行数内,trans不报错,但通过错误处理步骤捕捉,并记入文件,整个数据管线正常完成直至处理完最后一个输入行. 解决方案:使用步骤[数据检验]进行 ...
- FinalReference 如何使 GC 过程变得拖拖拉拉
本文基于 OpenJDK17 进行讨论,垃圾回收器为 ZGC. 提示: 为了方便大家索引,特将在上篇文章 <以 ZGC 为例,谈一谈 JVM 是如何实现 Reference 语义的> 中讨 ...
- Maven配置阿里云镜像和本地仓库路径
配置阿里云镜像仓库 在settings > mirrors标签下添加以下内容 <!-- Aliyun Mirror --> <mirror> <id>alim ...
- 记录用C#写折半查找算法实现
折半查找算法 前言 最近要考试了,重新回顾一下之前学的算法,今天是折半查找,它的平均比较次数是Log2 n 思想 给定一个有序数组A[0..n-1],和查找值K,返回K在A中的下标. 折半查找需要指定 ...
- 高通Android UEFI中的LCD分析(2):关键的函数
# 高通Android UEFI中的LCD分析(2):关键的函数 背景 在启动流程分析中,看到了几个经常出现的函数,这里实际分析一下有关的实现.以搞清楚高通做了什么,以及我们能做什么. 重要函数 MD ...
- 高通参考设计中MTP与QRD
高通参考设计中MTP与QRD 背景 之前在调试设备树的时候,看到设备树带了一个qrd的后缀,一直没搞清楚.上网找资料也好像不是我想要的. 今天查阅lk侧的代码,发现了HW_PLATFORM_HRD这个 ...
- 不是人家太装逼,而是我们太low
在一个社团的迎新的时候,每个人自我介绍.等到一个一身LV,爱马仕的女孩子自我介绍,说起爱好,她想了想说:喜欢跑车.然后很淡定的坐下了.很多同学你看我我看你,投以"炫富"的判断目光- ...
- 如何让其他模型也能在SemanticKernel中调用本地函数
在SemanticKernel的入门例子中: // Import packages using Microsoft.SemanticKernel; using Microsoft.SemanticKe ...