题解 P3224 [HNOI2012]永无乡
时间:2021-08-12
本文章向大家介绍题解 P3224 [HNOI2012]永无乡,主要包括题解 P3224 [HNOI2012]永无乡使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
前置知识:kruskal 重构树,主席树
来讲一种目前题解区里没有,使用 kruskal 重构树和主席树,且时间复杂度为一个 log 的做法。
题目大意是给定一张点数为 \(n\) ,初始边数为 \(m\) 的无向图,图中每个点有一个权值,然后有 \(q\) 个操作,每个操作可能是询问与点 \(x\) 联通的点中权值第 \(k\) 小的点的编号,也可能是加入一条无向边。
看到这种与联通性相关的题,自然能想到 kruskal 重构树,于是把操作离线,连边操作直接丢给 kruskal 重构树,把其对应点的限制设为时间戳,然后将询问操作也带上时间戳存下来。
这样我们就解决了动态加边的问题,再给 kruskal 重构树加个倍增,我们就可以快速得到一个询问所对应的点集了。
询问的是第 \(k\) 小,于是在 kruskal 重构树上再跑个 dfs 序,弄颗主席树,此题就做完啦 ^_^
时间复杂度:\(O(qlog(n))\)
此外,实现时有个坑点,这张图不一定联通,所以 dfs 时一定要把每个根都跑了。
参考代码:
#include <bits/stdc++.h>
#define rei register int
#define ll long long
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5, M = 1e5 + 5, MAXQ = 3e5 + 5;
int n, m, Q, tot, totq, num;
int val[N * 2], fa[N * 2], f[N * 2][19], lim[N * 2];
int lson[N * 2], rson[N * 2], siz[N * 2];
int id[N * 2], key[N];
struct Segment_Tree {
int tot, ver[N * 2];
struct node {
int cnt, ls, rs;
} T[N * 39];
int build(int l, int r) {
int nnow = ++tot;
if(l == r) return nnow;
int mid = (l + r) >> 1;
T[nnow].ls = build(l, mid);
T[nnow].rs = build(mid + 1, r);
return nnow;
}
int add(int x, int k, int l, int r, int now) {
int nnow = ++tot;
T[nnow] = T[now];
if(l == r) {
T[nnow].cnt += k;
return nnow;
}
int mid = (l + r) >> 1;
int &L = T[nnow].ls, &R = T[nnow].rs;
if(x <= mid) L = add(x, k, l, mid, T[now].ls);
else R = add(x, k, mid + 1, r, T[now].rs);
T[nnow].cnt = T[L].cnt + T[R].cnt;
return nnow;
}
int ask(int k, int l, int r, int now1, int now2) {
if(l == r) {
return key[l];
}
int L1 = T[now1].ls, R1 = T[now1].rs;
int L2 = T[now2].ls, R2 = T[now2].rs;
int mid = (l + r) >> 1;
if(k <= T[L2].cnt - T[L1].cnt) {
return ask(k, l, mid, L1, L2);
} else {
return ask(k - T[L2].cnt + T[L1].cnt, mid + 1, r, R1, R2);
}
}
} SMT;
int find(int x) {
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void link(int u, int v, int l) {
if(tot == 2 * n - 1) return;
u = find(u), v = find(v);
if(u == v) return;
fa[u] = fa[v] = f[u][0] = f[v][0] = ++tot;
fa[tot] = f[tot][0] = tot;
lim[tot] = l, val[tot] = inf;
lson[tot] = u, rson[tot] = v;
}
struct query {
int x, k, high;
query(int x = 0, int k = 0, int high = 0) {
this->x = x, this->k = k, this->high = high;
}
} q[MAXQ];
void dfs(int x) {
id[x] = ++num, siz[x] = 1;
if(val[x] == inf) SMT.ver[num] = SMT.ver[num - 1];
else key[val[x]] = x, SMT.ver[num] = SMT.add(val[x], 1, 1, n, SMT.ver[num - 1]);
if(lson[x]) dfs(lson[x]), siz[x] += siz[lson[x]];
if(rson[x]) dfs(rson[x]), siz[x] += siz[rson[x]];
}
int solve(query pb) {
int x = pb.x, k = pb.k, high = pb.high;
for(rei i = 17; i >= 0; --i) {
if(lim[f[x][i]] < high) {
x = f[x][i];
}
}
int l = SMT.ver[id[x] - 1], r = SMT.ver[id[x] + siz[x] - 1];
if(k > SMT.T[r].cnt - SMT.T[l].cnt) return -1;
return SMT.ask(k, 1, n, l, r);
}
int main() {
ios::sync_with_stdio(false), cin.tie(NULL);
cin >> n >> m;
tot = n;
for(rei i = 1; i <= n; ++i) {
cin >> val[i];
fa[i] = f[i][0] = i;
}
for(rei i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
link(u, v, 0);
}
cin >> Q;
for(rei i = 1; i <= Q; ++i) {
char op; int x, y;
cin >> op >> x >> y;
if(op == 'B') {
link(x, y, i);
} else {
q[++totq] = query(x, y, i);
}
}
for(rei j = 1; j <= 17; ++j) {
for(rei i = 1; i <= tot; ++i) {
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
SMT.ver[0] = SMT.build(1, n);
for(rei i = 1; i <= tot; ++i) {
if(fa[i] == i) dfs(i);
}
for(rei i = 1; i <= totq; ++i) {
cout << solve(q[i]) << "\n";
}
return 0;
}
无耻求波赞 QAQ
原文地址:https://www.cnblogs.com/Szzkp/p/15135119.html
- 微信公众平台增加批量获取用户基本信息接口
- 谈网络适配器
- 【框架】为降低机器学习开发者门槛,苹果发布了Turi Create框架
- 新闻数据库分表案例
- 建立智能的解决方案:将TensorFlow用于声音分类
- Plugin Hook 设计与实现
- 数据与应用程序间通信·UDP Socket
- Java 数据类型转换
- Spring boot with Scheduling
- Spring Properties 文件读取
- 【学术】你真的知道什么是随机森林吗?本文是关于随机森林的直观解读
- Spring boot 将 Session 放入 Redis
- 【教程】估算一个最佳学习速率,以更好地训练深度神经网络
- SNS 数据库设计
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Struts2自定义结果视图(servlet验证码)
- jQuery Ajax传递数组到asp.net web api参数为空
- asp.net web api集成微信服务(使用Senparc微信SDK)
- asp.net web api添加统一异常处理
- .NET HttpClient扩展
- md5和base64加密解密
- asp.net web api添加自定义认证
- 代理模式实例
- FastDFS.Client操作文件服务器
- Oracle触发器实现监控某表的CRUD操作
- asp.net web api返回图片至前端
- sql模糊匹配中%、_的处理
- Dapper关联查询
- Vue2.0 + Element-UI + WebAPI实践:简易个人记账系统
- java获取时间整点工具代码