前言

题目链接:洛谷CF

题意简述

yzh 喜欢单调不降序列。

她有一个序列 \(a\),最初为 \(a_1, \ldots, a_n\),其中每个元素都在 \([1, m]\) 内。

她希望使序列变得单调不降,为此,她有一个序列 \(b_1, \ldots, b_m\),每个元素也在 \([1, m]\) 内。她可以进行若干次操作,一次操作定义为:

  1. 选择一个集合 \(S \subseteq \lbrace 1, 2, \ldots, n \rbrace\)。
  2. \(\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\),形成一个内向基环树森林。

  1. 当 \(xym\) 和 \(yzh\) 不在一棵基环树内时,\(\operatorname{dist}(xym, yzh) = \infty\)。
  2. 当 \(xym\) 和 \(yzh\) 在同一棵基环树内时:
    1. \(yzh\) 是 \(xym\) 的祖先,\(\operatorname{dist}(xym, yzh) = dpt_{xym} - dpt_{yzh}\)。
    2. \(yzh\) 是环上一点,设 \(xym\) 是环上 \(p\) 的子孙,\(\operatorname{dist}(xym, yzh) = dpt_{xym} - dpt_{p} + \operatorname{dis}(p, yzh)\),实现环上距离 \(\operatorname{dis}(a, b)\) 是 naive 的。
    3. 其他情况 \(\operatorname{dist}(xym, yzh) = \infty\)。

到此为止,我们已经能完成这道题了。但是,我要讲另一种更方便求得基环树森林中两点距离的方法,一下只考虑 \(xym\) 和 \(yzh\) 在同一棵基环树内。

考虑拆环成树,断开环上任意一条边 \(u \rightarrow b_u\),再以 \(u\) 为根,做一遍内向树上的深搜。考虑这时候计算 \(\operatorname{dist}(xym, yzh)\)。

  1. 若 \(xym \in \operatorname{subtree}(yzh)\),则 \(\operatorname{dist}(xym, yzh) = dpt_{xym} - dpt_{yzh}\),\(\operatorname{subtree}(yzh)\) 可以用 dfs 序实现。
  2. 若 \(\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}\)。
  3. 其他情况 \(\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 题解的更多相关文章

  1. UVA 10131题解

    第一次写动态规划的代码,整了一天,终于AC. 题目: Question 1: Is Bigger Smarter? The Problem Some people think that the big ...

  2. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  3. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  4. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  5. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  6. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  7. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  8. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  9. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  10. CF100965C题解..

    求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...

随机推荐

  1. Idea SpringBoot 子模块 加载不到该子模块根目录config下面的配置文件

    Idea SpringBoot 子模块 加载不到该子模块根目录config下面的配置文件 import org.mybatis.spring.annotation.MapperScan; import ...

  2. java redis api及test demo

    1.CacheService.java package com.redis.demo; import com.alibaba.fastjson.JSON; import com.alibaba.fas ...

  3. C++与Unity C#交互

    C++与Unity C#交互 C++转C#小工具:https://github.com/jaredpar/pinvoke-interop-assistant C++ Custom.h #pragma ...

  4. 技嘉BIOS超频设置操作路径

    关闭超线程 频率电压控制 > 进阶处理器设置 > Hyper_THreading 关小核心 频率电压控制 > GIGABYTE PerfDrive > Ecore Disabl ...

  5. Jmeter自动录制脚本

    1.Jmeter配置 1.1新增一个线程组 1.2Jmeter中添加HTTP代理 1.3配置HTTP代理服务器 修改端口 修改Target Cintroller(目标控制器) 修改Grouping(分 ...

  6. 记一次 Edge 及谷歌 Chrome 浏览器兼容性冲突的解决

    目录 记一次 Edge 及谷歌 Chrome 浏览器兼容性冲突的解决 浏览器兼容性冲突症状 解决方法 1. 把本机和远程的 8235 和 8237 端口屏蔽,包括 TCP 和 UDP 端口 2. 在 ...

  7. SpringBoot动态数据源配置

    SpringBoot动态数据源配置 序:数据源动态切换流程图如下: 1:pom.xml文件依赖声明 <dependency>   <groupId>org.springfram ...

  8. Linux内核的5个子系统

    --- title: Linux内核的5个子系统 EntryName: subsystems_in_linux_kernel date: 2020-10-10 03:07:07 categories: ...

  9. python 无监督生成模型

    无监督生成模型在机器学习中扮演着重要角色,特别是当我们在没有标签数据的情况下想要生成新的样本或理解数据的内在结构时.一种流行的无监督生成模型是生成对抗网络(Generative Adversarial ...

  10. Ubuntu20.04之Nvidia驱动安装

    参考:https://blog.csdn.net/xiaokedou_hust/article/details/82187860,实际操作时和该博文有些出入,故作优化. s1.连接wifi,打开终端. ...