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

对于一张图来说,两个人能满足题面关系等价于这张图不是链,很好证明,如果有度数 \(> 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. 面试都要问的Spring MVC

    MVC总结 1. 概述 还是之前的三个套路 1.1 是什么? Spring提供一套视图层的处理框架,他基于Servlet实现,可以通过XML或者注解进行我们需要的配置. 他提供了拦截器,文件上传,CO ...

  2. mybatis insert转update,duplicate关键字的使用示例,及返回情况说明

    主键存在时又insert转为update某个关键字段,示例如下,注意,如果这条数据曾经不存在,此时执行insert返回条目是1,如果已存在,执行update返回条目是2!!!<insert id ...

  3. Ceph根据Crush位置读取数据

    前言 在ceph研发群里面看到一个cepher在问关于怎么读取ceph的副本的问题,这个功能应该在2012年的时候,我们公司的研发就修改了代码去实现这个功能,只是当时的硬件条件所限,以及本身的稳定性问 ...

  4. python + selenium 搭建环境步骤

    介绍在windows下,selenium python的安装以及配置.1.首先要下载必要的安装工具. 下载python,我安装的python3.0版本,根据你自己的需要安装 下载setuptools ...

  5. 小而精的 Docker 项目,为什么要使用 Docker? Docker 容器

    前言 为什么要使用 Docker? Docker 容器的启动在秒级 Docker 对系统资源利用率高,一台主机上可以同时运行数千个 Docker 容器. Docker 基本不消耗系统资源,使得运行在 ...

  6. 我要进大厂之大数据ZooKeeper知识点(2)

    01 我们一起学大数据 接下来是大数据ZooKeeper的比较偏架构的部分,会有一点难度,老刘也花了好长时间理解和背下来,希望对想学大数据的同学有帮助,也特别希望能够得到大佬的批评和指点. 02 知识 ...

  7. 计算思维(美国CMU周以真教授)

    博主注:GIScience会议是国际上最为著名的地理信息系统领域的国际会议,自2000年起,每两年举办一次,GIScience 2008会议邀请了美国卡内基-梅隆大学(CMU)计算机系华裔教授周以真博 ...

  8. 【PUPPETEER】初探之元素获取(二)

    一.涉及的知识点 如何使用css selector 常用元素获取 $ 元素选择 type (api 输入) click (api 点击) 二.学习网址 https://github.com/Googl ...

  9. 通过jquery 获取下拉列表中选中的值对应的value

    <div class="col-sm-9"> <select id="device-type" class="form-contro ...

  10. python批量生成SQL语句

    1,首先写一条能运行成功插入SQL的语句 INSERT INTO sign_guest(realname,phone,email,sign,event_id)VALUES("jack&quo ...