前言

题目链接:洛谷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. 三月二十六日 安卓打卡app开发日志

    今天上午 将打卡逻辑代码优化了一下 之后每天就只可以打卡一次了 public static String daka(String time_s, String time_e, String text, ...

  2. fs.1.10 ON rockeylinux8 dockerfile模式

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. rockeylinux8 docker上编译安装fs.1.10的流程记录,本文使用dockerfile模式. 环境 docker e ...

  3. MyBatis的逆向工程详细步骤操作

    1. MyBatis的逆向工程详细步骤操作 @ 目录 1. MyBatis的逆向工程详细步骤操作 2. 逆向工程配置与生成 2.1 MyBatis3Simple:基础版,只有基本的增删改查 2.1.1 ...

  4. CloseableHttpClient 连接超时导致XxlJob调度阻塞,影响调度任务的执行

    CloseableHttpClient 连接超时导致XxlJob调度阻塞,影响调度任务的执行 问题原因1.分析日志发现,xxlJob后台界面没有执行时间和执行结果,在某一个时间点之后,某一个任务因为阻 ...

  5. SQL索引优化,菜单列表优化

    SQL索引优化,菜单列表优化 现象:在系统中几个数据量大的列表页面,首次进入页面未增加筛选条件,导致进入的列表查询速度非常慢.分析:通过SQL查看,是做了count求和查询,然后根据总的记录数来做分页 ...

  6. 纯代码搭建iOS三级结构(UITabbarController+UINavigationController+UIViewController)

    声明:这里所指的三级结构不是网上百度中所经常提及的三级框架或者MVC模式,而是指UITabbarController+UINavigationController+UIViewController. ...

  7. Springboot+Shiro+Mybatis+mysql实现权限安全认证

    Shiro是Apache 的一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.Shiro 主要分为两个部分就是认证和授权两部分 一.介绍 Subject代表了当前用户的安全操作 ...

  8. Blazor Server App Cannot find the fallback endpoint specified by route values

    github官方issues中提到的解决方案,CreateBuilder时指定项目绝对路径可以解决. 1 // 指定项目路径,也可以用Assembly.GetCallingAssembly获取 2 c ...

  9. Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?

    受现代 JavaScript 的限制 ,Vue 无法检测到对象属性的添加或删除.由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 ...

  10. python配置国内pypi镜像源操作步骤

    使用pip config命令设置默认镜像源,使用国内的源,提高安装速度 操作步骤 临时方式pip install xxx -i https://pypi.tuna.tsinghua.edu.cn/si ...