我是真的不稳定的垃圾选手。

对于一张图来说,两个人能满足题面关系等价于这张图不是链,很好证明,如果有度数 \(> 2\) 的点,让一个人跑到一个度数 \(= 1\) 的地方就可以了。

如果离线就可以什么启发式合并 + 并查集做到一个 \(\log\)。就是合并两个点 \(x, y\),动态维护每个联通块是不是链。

  • 连接两个已经在同一个联通块的点变成环就不是链了
  • 若 \(x, y\) 之前所在联通块不是链总共也不是链了
  • 若 \(x, y\) 其中有一个是不是端点总共也不是链了。
  • 如果 \(x\) 所在联通块大小 \(> 1\) 那么 \(x\) 就不是端点了,\(y\) 同理。

然后这是强制在线的。。可持久化并查集,询问的时候要二分,每次 check 还是两个 \(\log\),最后 \(O(m \log^2 n + q \log ^ 3 n)\),居然只会 TLE 五六个点。。。然后我也不会卡常,自闭了。

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std; const int MAXN = 100005, MAXM = 200005; int n, m, arr[MAXM], tot; struct E{
int u, v, w;
bool operator < (const E &b) const {
return w < b.w;
}
} e[MAXM]; struct T{
int l, r, v;
}; struct Array{
int rt[MAXM], idx;
T t[MAXN * 22];
void build(int &p, int l, int r, int o) {
p = ++idx;
if (l == r) { t[p].v = o == -1 ? r : o; return ; }
int mid = (l + r) >> 1;
build(t[p].l, l, mid, o);
build(t[p].r, mid + 1, r, o);
}
void update(int &p, int q, int l, int r, int x, int o) {
t[p = ++idx] = t[q];
if (l == r) { t[p].v = o; return; }
int mid = (l + r) >> 1;
if (x <= mid) update(t[p].l, t[q].l, l, mid, x, o);
else update(t[p].r, t[q].r, mid + 1, r, x, o);
}
int query(int p, int l, int r, int x) {
if (l == r) return t[p].v;
int mid = (l + r) >> 1;
if (x <= mid) return query(t[p].l, l, mid, x);
else return query(t[p].r, mid + 1, r, x);
}
void inline set(int x, int k, int i) { update(rt[i], rt[i], 0, n - 1, x, k); }
int inline get(int x, int i) { return query(rt[i], 0, n - 1, x); }
} p, f, d, sz; int find(int x, int i) {
int fa = f.get(x, i);
return x == fa ? x : find(fa, i);
} void inline merge(int x, int y, int i) {
int a = find(x, i), b = find(y, i);
if (a == b) {
p.set(a, 1, i);
} else {
int sa = sz.get(a, i), sb = sz.get(b, i);
if (sa > sb) swap(sa, sb), swap(a, b), swap(x, y);
f.set(a, b, i);
int dx = d.get(x, i), dy = d.get(y, i);
if (p.get(a, i) || !dx || !dy) p.set(b, 1, i);
if (dx && sa > 1) d.set(x, 0, i);
if (dy && sb > 1) d.set(y, 0, i);
sz.set(b, sa + sb, i);
}
} void init(int N, int M,
std::vector<int> U, std::vector<int> V, std::vector<int> W) {
n = N, m = M;
for (int i = 0; i < m; i++) {
arr[++tot] = W[i];
e[i] = (E) { U[i], V[i], W[i] };
}
sort(arr + 1, arr + 1 + tot);
tot = unique(arr + 1, arr + 1 + tot) - arr - 1;
sort(e, e + m);
f.build(f.rt[0], 0, n - 1, -1), sz.build(sz.rt[0], 0, n - 1, 1);
p.build(p.rt[0], 0, n - 1, 0), d.build(d.rt[0], 0, n - 1, 1);
for (int i = 1, j = 0; i <= tot; i++) {
p.rt[i] = p.rt[i - 1], f.rt[i] = f.rt[i - 1];
d.rt[i] = d.rt[i - 1], sz.rt[i] = sz.rt[i - 1];
while (j < m && e[j].w <= arr[i]) {
merge(e[j].u, e[j].v, i);
++j;
}
}
} bool inline check(int X, int Y, int i) {
int a = find(X, i), b = find(Y, i);
return a == b && p.get(a, i);
} int getMinimumFuelCapacity(int X, int Y) {
if (!check(X, Y, tot)) return -1;
int l = 1, r = tot;
while (l < r) {
int mid = (l + r) >> 1;
if (check(X, Y, mid)) r = mid;
else l = mid + 1;
}
return arr[r];
}

然后就去无耻的看题解,然后去学了一下 Kruscal 重构树

我们不妨设计一个特殊的 Kruscal 重构树,使得对于 \(x, y\) 的询问,答案就是他们的 LCA ,即他们的 LCA 是合并过程中让他们从链变成不是链的那条关键边!

那么,设计一种类似于普通 Kruscal 最小重构树的方式,只不过改动如下:

  • 对于合并还是链的联通块,先不要在重构树上连边。
  • 当一条链 \(\Rightarrow\) 不是链的时候,把这条边的新点是链里所有点两两的关键边,所以把这个新点作为这条链上所有点的父亲就行了!这样的意义是这些点两两关键边就是这个边。

发现这么构造仍然满足大根堆性质。

用启发式合并存储当前联通块的所有点,每个原始点只会连一次,即连向他所在联通块转变的关键边新点,所以复杂度是 \(O(n \log n)\) 的。

正确性:在 LCA 这个子树中,所有边都是 \(\le w_{LCA}\) 的,LCA 的子树形成的结构不是链,因为只有不是链才会连边,所以必然 LCA 的权值是可以的。然后证明 \(ans \ge w_{LCA}\)。\(ans < w_{LCA}\) 显然是不行的,因为他们要用这条关键边。所以 \(ans = w_{LCA}\)。

所以每次询问求个 LCA 就行了。

时间复杂度 \(O((n + q) \log n)\)

我代码偷了一下懒,即只在链的时候维护各种信息,已经不是链了,除了 \(f\) 数组剩下就不用维护。

#include "swap.h"

#include <algorithm>
#include <vector>
#include <iostream> using namespace std; const int N = 100005, M = 200005, L = 19; int n, m, f[N << 1], fa[N << 1][L], dep[N << 1], cnt, sz[N], a[N << 1]; bool ch[N << 1], d[N]; vector<int> g[N << 1];
vector<int> c[N << 1]; struct E {
int u, v, w;
bool operator<(const E &b) const { return w < b.w; }
} e[M]; int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); } void dfs(int u) {
for (int i = 1; i < L && fa[u][i - 1]; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
fa[v][0] = u, dep[v] = dep[u] + 1;
dfs(v);
}
} int lca(int x, int y) {
if (dep[x] < dep[y])
swap(x, y);
for (int i = L - 1; ~i; i--)
if (dep[x] - (1 << i) >= dep[y])
x = fa[x][i];
if (x == y)
return x;
for (int i = L - 1; ~i; i--)
if (fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][0];
} void inline merge(int u, int v, int w) {
int x = find(u), y = find(v);
if (x == y) {
if (ch[x]) {
a[++cnt] = w;
for (int i = 0; i < sz[x]; i++) g[cnt].push_back(c[x][i]);
f[x] = cnt, ch[cnt] = false;
}
} else {
if (sz[x] > sz[y])
swap(x, y), swap(u, v);
if (!ch[x] || !ch[y] || !d[u] || !d[v]) {
a[++cnt] = w;
if (ch[y])
for (int i = 0; i < sz[y]; i++) g[cnt].push_back(c[y][i]);
else
g[cnt].push_back(y);
if (ch[x])
for (int i = 0; i < sz[x]; i++) g[cnt].push_back(c[x][i]);
else
g[cnt].push_back(x);
f[x] = f[y] = cnt;
} else {
if (sz[x] > 1)
d[u] = false;
if (sz[y] > 1)
d[v] = false;
f[x] = y, sz[y] += sz[x];
for (int i = 0; i < sz[x]; i++) c[y].push_back(c[x][i]);
}
}
} void init(int N, int M, std::vector<int> U, std::vector<int> V, std::vector<int> W) {
n = cnt = N, m = M;
for (int i = 1; i <= m; i++) e[i] = (E){ U[i - 1] + 1, V[i - 1] + 1, W[i - 1] };
sort(e + 1, e + 1 + m);
for (int i = 1; i < 2 * n; i++) {
f[i] = i;
if (i <= n)
sz[i] = 1, c[i].push_back(i), ch[i] = true, d[i] = true;
}
for (int i = 1; i <= m; i++) merge(e[i].u, e[i].v, e[i].w);
if (cnt > n)
dfs(cnt);
} int getMinimumFuelCapacity(int X, int Y) {
++X, ++Y;
if (ch[find(X)])
return -1;
return a[lca(X, Y)];
}

APIO2020 交换城市的更多相关文章

  1. 【APIO2020】交换城市(Kruskal重构树)

    Description 给定一个 \(n\) 个点,\(m\) 条边的无向连通图,边带权. \(q\) 次询问,每次询问两个点 \(x, y\),求两点间的次小瓶颈路.不存在输出 -1. Hint \ ...

  2. [loj3346]交换城市

    观察可得,$(x,y)$能相互到达当且仅当:1.$x$和$y$联通:2.$x$和$y$所在的连通块不为链 根据这个结论,可以二分枚举答案+暴力判定,复杂度$o(qm\log_{2}1e9)$,可以通过 ...

  3. 微信小程序开发心得

    微信小程序也已出来有一段时间了,最近写了几款微信小程序项目,今天来说说感受. 首先开发一款微信小程序,最主要的就是针对于公司来运营的,因为,在申请appid(微信小程序ID号)时候,需要填写相关的公司 ...

  4. 微信小程序开发心得--动画机制

    微信小程序也已出来有一段时间了,最近写了几款微信小程序项目,今天来说说感受.首先开发一款微信小程序,最主要的就是针对于公司来运营的,因为,在申请appid(微信小程序ID号)时候,需要填写相关的公司认 ...

  5. 城市经纬度 json 理解SignalR Main(string[] args)之args传递的几种方式 串口编程之端口 多线程详细介绍 递归一个List<T>,可自己根据需要改造为通用型。 Sql 优化解决方案

    城市经纬度 json https://www.cnblogs.com/innershare/p/10723968.html 理解SignalR ASP .NET SignalR 是一个ASP .NET ...

  6. 移动GIS技术在城市信息采集中的应用

    1 引言 随着移动平板电脑和手机(以下简称移动终端)在软硬件上的更新换代,和3G.4G通讯网络的升级,传统测绘和和数据服务方式正在发生巨大变化.以城市中的外业踏勘和信息采集为例,移动终端正成为主要的外 ...

  7. 【BZOJ3091】城市旅行 LCT

    [BZOJ3091]城市旅行 Description Input Output Sample Input 4 5 1 3 2 5 1 2 1 3 2 4 4 2 4 1 2 4 2 3 4 3 1 4 ...

  8. “智慧海绵城市”(SSC)监测评价体系整体解决方案

    一.方案简介 无论是内涝防治.黑臭水体治理,还是海绵城市规划设计及建设.评估,乃至未来智慧城市的建设,都需要有全面.致密.大量的城市水文监测数据和先进模拟仿真技术作基础支撑,唯有如此,决策才有据可依, ...

  9. BIM与GIS融合的意义——从智慧工地到智慧城市

    随着智慧城市概念的发展,BIM与GIS融合的概念深入人心,通过整合BIM的参数化描述建筑组件性质的特性与GIS宏观的几何空间概念,将 BIM 描述单体建筑物的特性通过 GIS 拓展至三维城市. BIM ...

随机推荐

  1. Linux杂谈: 实现一种简单实用的线程池(C语言)

    基本功能 1. 实现一个线程的队列,队列中的线程启动后不再释放: 2. 没有任务执行时,线程处于pending状态,等待唤醒,不占cpu: 3. 当有任务需要执行时,从线程队列中取出一个线程执行任务: ...

  2. kubernetes存储类与PV与PVC关系及实践

    StorageClass & PV & PVC关系图 Volumes是最基础的存储抽象,其支持多种类型,包括本地存储.NFS.FC以及众多的云存储,我们也可以编写自己的存储插件来支持特 ...

  3. spring中的事务有两种方式

    1种是我们常用的声明式事务,如注解,或者配置文件配置的. 2种是编程式事务,如 TransactionTemplate 类的使用.

  4. Codeforces741D

    dsu on tree 题目链接 点我跳转 题目大意 一棵根为 \(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种) 一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经 ...

  5. 数据恢复当选EasyRecovery,设备不再受限

    我们在逐渐适应信息电子化的同时,也有一些潜在的麻烦接踵而来,其中较为常见的就是文件和数据的保存问题. 显然,设备的存储空间是有限的,这就不可避免地会出现数据被删除.覆盖或丢失的现象,如果丢失的是重要数 ...

  6. Improving Commonsense Question Answering by Graph-based Iterative Retrieval over Multiple Knowledge Sources —— 基于多知识库迭代检索的常识问答系统

    基于多知识库迭代检索的问答系统 论文地址 背景 常识问答任务需要引入外部知识来帮助模型更好地理解自然语言问题,现有的解决方案大都采用两阶段框架: 第一阶段 -- 从广泛的知识来源中找到与给定问题相关的 ...

  7. word查找与替换

    ------------恢复内容开始------------ 如何快速删除大量空格键:查找和替换-更多-特殊格式-查找内容[特殊格式(段落标记)]设置为(^p^p,即点击两次段落标记),替换设置为(^ ...

  8. Kafka作为分布式消息系统的系统解析

    Kafka概述 Apache Kafka由Scala和Java编写,基于生产者和消费者模型作为开源的分布式发布订阅消息系统.它提供了类似于JMS的特性,但设计上又有很大区别,它不是JMS规范的实现,如 ...

  9. markdown语法和数学公式

    目录 Markdown简介 代码块 LaTeX 公式 表格 LaTeX 矩阵公式 Markdown简介 Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格 ...

  10. C和指针课后练习题4

    1.下面表达式是否合法?如果合法,他执行什么任务? 3* x * x - 4 * x + 6; 合法;他只是执行了表达式求值,但是他的结果并不存于任何地方. 2.赋值语句的语法? 数据类型 变量名 = ...