【APIO2019】桥梁(询问分块)
Description
给定一张 \(n\) 个点,\(m\) 条边的无向图,边 \(i\) 的权值为 \(d_i\)。现有 \(q\) 次操作,第 \(j\) 个操作有两种模式:
- \(1\ b_j\ r_j\):将第 \(b_j\) 条边的权改为 \(r_j\)。
- \(2\ s_j\ w_j\):询问与点 \(s_j\) 连通的点的数量(包括自己)。若边 \((u, v)\) 的权值 \(d \ge w_j\),那么视作 \(u, v\) 连通。若 \((a, b), (b, c)\) 分别连通,那么 \((a, c)\) 也连通。
对于每个 \(2\) 操作输出答案。
Hint
- \(1\le n\le 5\times 10^4\)
- \(0\le m\le 10^5\)
- \(1\le q\le 10^5\)
- \(d, r, w\in [1, 10^9]\)
Solution
先看一个比较显然的部分分做法:将询问离线,按 \(w\) 排序,边按 \(d\) 排序,降序。如果没有询问那这个就是 naive,而正好有这档部分分。考虑修改:将边按是否有修改分类,无修直接加,带修在处理询问时暴力枚举,根据操作的时间做修改,然后一个个加入并查集,最后撤回。由于需要可撤销,需要启发式合并。复杂度 \(O(q\log q + m\log m + q^2\log n)\)。数据分治期望有 \(27\) pts。
一次性枚举所有询问,这就是复杂度瓶颈所在。但如果直接暴力就得扫一遍所有边。
基于上述解法,考虑根号平衡的思想来优化——对询问分块!
设块长为 \(s\)。对于当前块,我们默认前面块已经处理好修改操作了,那么就不需要再考虑前面的块的修改。首先任然是两次排序,一次所有边,一次 块内的询问,那么复杂度是 \(O(m\log m + s\log s)\)。枚举询问时,对于所有无修边,直接加入;对于带修边,由分了块,带修边不会太多,我们直接枚举,把有效的边插入,统计答案后再撤销即可,这里复杂度为 \(O(s^2\log s)\)。
当前块处理完后,清空询问与修改。这样的算法总复杂度为 \(O(s(s^2\log s + s\log s + m\log m)) = O(s^3\log s + sm\log m)\)。
当 \(s = \sqrt{q}\) 是,复杂度为 \(O(q\sqrt{q}\log q + m\sqrt{q} \log m)\)。
实现细节较多,需要注意。
Code
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : APIO2019 桥梁
*/
#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
const int N = 5e4 + 5;
const int M = 1e5 + 5;
int n, m, q;
struct Edge {
int u, v, w, idx;
} e[M];
struct Command {
int pos, val, idx;
}; vector<Command> Q, E;
int ans[M];
int fa[N], siz[N], stk[N], top = 0;
int find(int x) {
return x == fa[x] ? x : find(fa[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return;
if (siz[x] < siz[y]) swap(x, y);
stk[++top] = y;
siz[x] += siz[y], fa[y] = x;
}
void backtrack(int time) {
while (top > time) {
int x = stk[top--];
siz[fa[x]] -= siz[x];
fa[x] = x;
}
}
void reset() {
for (int i = 1; i <= n; i++)
fa[i] = i, siz[i] = 1;
top = 0;
}
int cur[M];
int ref[M];
void solve() {
sort(e + 1, e + 1 + m, [](const Edge& a, const Edge& b) {
return a.w > b.w;
});
sort(Q.begin(), Q.end(), [](const Command& a, const Command& b) {
return a.val > b.val;
});
for (int i = 1; i <= m; i++)
ref[e[i].idx] = i;
reset();
vector<Command> E_tmp;
for (auto t : E) {
cur[t.pos] = -1;
E_tmp.push_back(Command{t.pos, e[ref[t.pos]].w, 0});
}
for (auto t : E) E_tmp.push_back(t);
for (int i = 0, j = 1; i < int(Q.size()); i++) {
for (; j <= m && e[j].w >= Q[i].val; j++)
if (!cur[e[j].idx]) merge(e[j].u, e[j].v);
for (auto t : E_tmp) if (t.idx <= Q[i].idx)
cur[t.pos] = t.val;
int bt = top; // backtrack
for (auto t : E) if (cur[t.pos] >= Q[i].val)
merge(e[ref[t.pos]].u, e[ref[t.pos]].v);
ans[Q[i].idx] = siz[find(Q[i].pos)];
backtrack(bt);
}
for (auto t : E) e[ref[t.pos]].w = t.val, cur[t.pos] = 0;
E.clear(), Q.clear();
}
signed main() {
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> e[i].u >> e[i].v >> e[i].w;
e[i].idx = i;
}
cin >> q;
int gap = int(sqrt(q)) << 2;
for (int i = 1; i <= q; i++) {
int op, pos, val;
cin >> op >> pos >> val;
if (op == 1) E.push_back(Command{pos, val, i});
else Q.push_back(Command{pos, val, i});
if (i % gap == 0) solve();
}
if (q % gap != 0) solve();
for (int i = 1; i <= q; i++)
if (ans[i]) printf("%d\n", ans[i]);
return 0;
}
【APIO2019】桥梁(询问分块)的更多相关文章
- 【LOJ#3145】[APIO2019]桥梁(分块,并查集)
[LOJ#3145][APIO2019]桥梁(分块,并查集) 题面 LOJ 题解 因为某个\(\text{subtask}\)没判\(n=1\)的情况导致我自闭了很久的题目... 如果没有修改操作,可 ...
- BZOJ3720 Gty的妹子树 询问分块、主席树
传送门 学到了询问分块的科技-- 对于修改操作,每发生了\(S\)次修改就重构整棵树,小于\(S\)次的修改操作丢到一个队列里面. 对于每一次查询操作,先在主席树上查询当前子树内部大于\(k\)的节点 ...
- CodeForces 103D Time to Raid Cowavans 询问分块
Time to Raid Cowavans 题意: 询问 下标满足 a + b * k 的和是多少. 题解: 将询问分块. 将b >= blo直接算出答案. 否则存下来. 存下来之后,对于每个b ...
- P5443 [APIO2019]桥梁 [分块+并查集]
分块+并查集,大板子,没了. 并查集不路径压缩,可撤销,然后暴力删除 这样对于每个块都是独立的,所以直接搞就行了. 然后块内修改操作搞掉,就是单独的了 // powered by c++11 // b ...
- bzoj 3781: 小B的询问 分块
3781: 小B的询问 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 196 Solved: 135[Submit][Status] Descrip ...
- 题解-APIO2019桥梁
problem \(\mathrm {loj-3145}\) 题意概要:给定一张 \(n\) 点 \(m\) 边的无向图,边有边权,共 \(q\) 次操作,每次会将第 \(x\) 条边的权值改为 \( ...
- P5443 [APIO2019]桥梁
传送门 子任务 $4$ 告诉我们可以离线搞带权并查集 从大到小枚举询问,从大到小连边 如果没有修改操作就可以过了 但是有修改,考虑最暴力的暴力,搞可撤销并查集 同样先离线,从大到小处理询问时,按原边权 ...
- 题解 洛谷 P5443 【[APIO2019]桥梁】
考虑若只有查询操作,那么就可以构造\(Kruskal\)重构树,然后在线询问了,也可以更简单的把询问离线,把询问和边都按权值从大到小排序,然后双指针依次加入对于当前询问合法的边,用并查集维护每个点的答 ...
- 【对询问分块】【主席树】bzoj2683 简单题
对操作序列分块,每S次暴力重建主席树. 当S=sqrt(n*log(n))时,复杂度为O(m*sqrt(n*log(n))). 在线的. #include<cstdio> #include ...
随机推荐
- grep/字符/次数匹配/锚定符/小大括号/wc/tr/cut/sort/uniq
grep:正则表达式,文本过滤工具,能够实现以指定的"模式(Pattern)"逐行搜索文件中的内容,并将匹配到的行显示出来. 模式:是由正则表达式的元字符,其他字符组合起来的匹配字 ...
- this.getClass().getResource("") url path file 区别
首先注意 "/word/appointDismiss.docx" 前面一定要加 /,有一次我就是忘记加/ 查了半天错, 不能写成 "word/appointDismiss ...
- redis源码学习之slowlog
目录 背景 环境说明 redis执行命令流程 记录slowlog源码分析 制造一条slowlog slowlog分析 1.slowlog如何开启 2.slowlog数量限制 3.slowlog中的耗时 ...
- Spring Cloud Security OAuth2.0 认证授权系列(一) 基础概念
世界上最快的捷径,就是脚踏实地,本文已收录[架构技术专栏]关注这个喜欢分享的地方. 前序 最近想搞下基于Spring Cloud的认证授权平台,总体想法是可以对服务间授权,想做一个基于Agent 的无 ...
- 微信小程序生成二维码并且扫码跳转并且携带参数
话不多说,直接上代码,直接把APPID和APPSECRET改成自己的就能用了 <?php header('content-type:text/html;charset=utf-8'); //配置 ...
- 新鲜出炉!阿里巴巴,京东,美团面经汇总,已拿offer!
最近在后台收到了很多小伙伴的私信,说自己最近想好好准备一下,在不久后的秋招跳个槽往大厂冲击一下,想要我给大家整理出一份大厂面试题好用来做好准备.之前公司有点事比较忙就没回私信.最近好一点了我也是立马联 ...
- Mac小白用户都能体验Windows应用的轻量级软件
近期,苹果在WWDC大会上表示Mac电脑将转向ARM架构,这意味着为iPhone手机.iPad平板和Mac电脑应用APP提供了统一的可能性.也就是说,iPhone手机.iPad平板电脑的应用可能在Ma ...
- guitar pro系列教程(二十五):Guitar Pro教程之组织小节
上一章节我们讲述了关于Guitar Pro 7的主界面的相关功能的介绍,对于初学作曲,又是吉他的初学者,刚刚接触Guitar Pro时,很多的功能,符号,工具都市不熟悉的,这样在创作,使用的过程中就会 ...
- Java中类加载的过程
类加载过程 这里的加载过程是严格按照加载开始顺序进行的,注意是加载开始而不是加载完成.也就是有可能会有两个或几个阶段是同时进行的. 比如下面提到的验证过程中的符号引用验证是在解析阶段开始之后进行. 加 ...
- php-fpm和nginx配置
groupadd www-data useradd -g www-data www-data find / -name "php-fpm.conf" 把所有文件下的 改成www-d ...