考试遇到一道题:

有一棵n个点的有根树,每个点有一个颜色,每次询问给定一个点\(u\)和一个数\(k\),询问\(u\)子是多少个不同颜色节点的\(k\)级祖先。n<=500000。

显然对每一层建主席树可行,但还有更优雅的一种做法——\(DSU\)

所谓\(DSU\),是一类处理子树信息的问题的通解(\(O(n\log n\))

其主要过程是树剖后,沿重儿子向下,优先统计轻儿子,并在统计结束后删除对轻儿子统计信息,最后删除对重儿子统计信息。

参考代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <numeric>
#define R(a,b,c) for(register int a = (b); a <= (c); ++a)
#define nR(a,b,c) for(register int a = (b); a >= (c); --a)
#define Swap(a,b) ((a) ^= (b) ^= (a) ^= (b))
#define MP make_pair
#ifdef QWQ
#define D_e_Line printf("\n--------\n")
#define C_e(x) cout << (#x) << " : " << x << endl
#define D_e(x) cerr << (#x) << " : " << x << endl
#define Pause() system("pause")
#define FileOpen() freopen("in.txt", "r", stdin)
#define FileSave() freopen("out.txt", "w", stdout)
#include <assert.h>
#define TIME() fprint(stderr, "TIME : %.3lfms\n", (double)clock() / CLOCKS_PER_SEC)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#else
#define D_e_Line
#define D_e(x)
#define C_e(x)
#define Pause
#define FileOpen()
#define FileSave()
#define TIME()
#define dbg(...)
#endif
struct FastIO {
template<typename ATP> inline FastIO& operator >> (ATP &x) {
x = 0; int f = 1; char c;
for(c = getchar(); c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
while(c >= '0' && c <= '9') x = x * 10 + (c ^ '0'), c = getchar();
if(f == -1) x = -x;
return *this;
}
} io;
using namespace std;
template<typename ATP> inline ATP Max(ATP x, ATP y) {
return x > y ? x : y;
}
template<typename ATP> inline ATP Min(ATP x, ATP y) {
return x < y ? x : y;
}
template<typename ATP> inline ATP Abs(ATP x) {
return x < 0 ? -x : x;
}
#include <vector>
#include <set> const int N = 1e5 + 7;
const int base = 122777; char str[27];
inline unsigned long long HashVal(char *str) {
int len = strlen(str + 1);
unsigned long long s = 1;
R(i,1,len)
s = s * base + str[i] - 'a';
return s;
}
struct Edge {
int nxt, pre;
} e[N];
int head[N], cntEdge;
inline void add(int u, int v) {
e[++cntEdge] = (Edge){ head[u], v}, head[u] = cntEdge;
}
struct Ques {
int K, id;
Ques() {}
Ques(int _K, int _id) : K(_K), id(_id) {}
};
int ans[N];
vector<Ques> q[N];
set<int> tot[N];
unsigned long long val[N];
int son[N], dep[N], fa[N], siz[N];
inline void DFS_First(int u, int father) {
dep[u] = dep[father] + 1, fa[u] = father, siz[u] = 1;
for(register int i = head[u]; i; i = e[i].nxt){
int v = e[i].pre;
DFS_First(v, u);
siz[u] += siz[v];
if(!son[u] || siz[v] > siz[son[u]]) son[u] = v;
}
}
bool big[N];
inline void Clear(int u, int father) {
tot[dep[u]].clear();
for(register int i = head[u]; i; i = e[i].nxt){
int v = e[i].pre;
if(big[v]) continue; // if it' s a heavy son, ignore it
Clear(v, u);
}
}
inline void Calc(int u, int father) {
tot[dep[u]].insert(val[u]);
for(register int i = head[u]; i; i = e[i].nxt){
int v = e[i].pre;
if(big[v]) continue;
Calc(v, u);
}
}
int n;
inline void DSU(int u, int father, int flag) { // flag = 0 : light, 1 : heavy
for(register int i = head[u]; i; i = e[i].nxt){ // every light son
int v = e[i].pre;
if(v == son[u]) continue;
DSU(v, u, 0);
}
if(son[u]) DSU(son[u], u, 1), big[son[u]] = true; // heavy son
Calc(u, father);
for(vector<Ques>::iterator it = q[u].begin(); it != q[u].end(); ++it){
int t = dep[u] + it -> K;
if(t <= n + 1) ans[it -> id] = tot[t].size();
}
if(son[u]) big[son[u]] = false; // finished getting the information from the heavy son
if(!flag) Clear(u, father); // clear the information from the light son
} int main() {
freopen("ancestor.in", "r", stdin);
freopen("ancestor.out", "w", stdout);
//FileOpen();
//FileSave();
io >> n;
R(i,1,n){
scanf("%s", str + 1);
val[i] = HashVal(str);
int fa;
io >> fa;
add(fa, i);
}
int Q;
io >> Q;
R(i,1,Q){
int x, K;
io >> x >> K;
q[x].push_back(Ques(K, i));
}
DFS_First(0, 0);
DSU(0, 0, 0); R(i,1,Q){
printf("%d\n", ans[i]);
} return 0;
}
/*
5
alice 0
alice 1
bob 2
cindy 1
bob 2
3
2 1
1 2
1 1
*/

例题

咕咕咕~

参考资料

总结-DSU ON TREE(树上启发式合并)的更多相关文章

  1. dsu on tree 树上启发式合并 学习笔记

    近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...

  2. dsu on tree (树上启发式合并) 详解

    一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...

  3. dsu on tree[树上启发式合并学习笔记]

    dsu on tree 本质上是一个 启发式合并 复杂度 \(O(n\log n)\) 不支持修改 只能支持子树统计 不能支持链上统计- 先跑一遍树剖的dfs1 搞出来轻重儿子- 求每个节点的子树上有 ...

  4. dsu on tree(树上启发式合并)

    简介 对于一颗静态树,O(nlogn)时间内处理子树的统计问题.是一种优雅的暴力. 算法思想 很显然,朴素做法下,对于每颗子树对其进行统计的时间复杂度是平方级别的.考虑对树进行一个重链剖分.虽然都基于 ...

  5. 树上启发式合并(dsu on tree)学习笔记

    有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...

  6. 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)

    (这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...

  7. 神奇的树上启发式合并 (dsu on tree)

    参考资料 https://www.cnblogs.com/zhoushuyu/p/9069164.html https://www.cnblogs.com/candy99/p/dsuontree.ht ...

  8. CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 树上启发式合并(DSU ON TREE)

    题目描述 一棵根为\(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种). 一条简单路径被称为\(Dokhtar-kosh\)当且仅当路径上的字符经过重新排序后可以变成一个回文串. 求 ...

  9. 树上启发式合并(dsu on tree)

    树上启发式合并属于暴力的优化,复杂度O(nlogn) 主要解决的问题特点在于: 1.对于树上的某些信息进行查询 2.一般问题的解决不包含对树的修改,所有答案可以离线解决 算法思路:这类问题的特点在于父 ...

  10. hdu6191(树上启发式合并)

    hdu6191 题意 给你一棵带点权的树,每次查询 \(u\) 和 \(x\) ,求以 \(u\) 为根结点的子树上的结点与 \(x\) 异或后最大的结果. 分析 看到子树,直接上树上启发式合并,看到 ...

随机推荐

  1. 【Azure 存储服务】Java Azure Storage SDK V12使用Endpoint连接Blob Service遇见 The Azure Storage endpoint url is malformed

    问题描述 使用Azure Storage Account的共享访问签名(Share Access Signature) 生成的终结点,连接时遇见  The Azure Storage endpoint ...

  2. MASA Auth - SSO与Identity设计

    AAAA AAAA即认证.授权.审计.账号(Authentication.Authorization.Audit.Account).在安全领域我们绕不开的两个问题: 授权过程可靠:让第三方程序能够访问 ...

  3. unittest框架里的常用断言方法:用于检查数据

    1.unittest框架里的常用断言方法:用于检查数据. (1)assertEqual(x,y) 检查两个参数类型相同并且值相等.(2)assertTrue(x) 检查唯一的参数值等于True(3)a ...

  4. stm32f103ve+BH1750使用教程+oled(HAL库)

    1.硬件:BH1750模块+oled 2.代码:BH1750是标准的iic协议的外设,我这里单独有iic的文件,之后想要实现多个设备共用一个iic. BH1750.c 1 #include " ...

  5. MAC NGINX PHP XDEBUG

    1. 安装 homebrew 2. 安装nginx ; 终端运行 brew install nginx: 1)给nginx 设置管理员权限:如果不设置管理员权限,80端口是不能监听的: #这里的目录根 ...

  6. 谷歌浏览器Chrome官方下载地址

    经常看到朋友的电脑上安装是魔改的谷歌浏览器.这里将谷歌浏览器官方的下载地址放在这里.有需要的朋友可以自己去下载. 下载地址 Chrome最新稳定在线安装版:https://www.google.cn/ ...

  7. SQL Server 2008~2019版本序列号/密钥/激活码 汇总

    SQL Server 2019 Enterprise:HMWJ3-KY3J2-NMVD7-KG4JR-X2G8G Strandard:PMBDC-FXVM3-T777P-N4FY8-PKFF4 SQL ...

  8. TopoLVM: 基于LVM的Kubernetes本地持久化方案,容量感知,动态创建PV,轻松使用本地磁盘

    正文 研发测试场景下,一般追求的是一键快速起环境,横向动态复制,一人一套,随起随用,用完即走.作为使用方,其不用关心实际的物理资源是怎样的,环境起在哪里,只要声明自己的使用需求即可.但作为方案构建者以 ...

  9. C# --- SqlserverHelper帮助类、快速实现增删改查

    using System;using System.Data; using System.Data.SqlClient; namespace Demo.WorkerService { public c ...

  10. Tapdata 在线研讨会:如何快速上手 Tapdata Cloud?

    偶然接触到 Tapdata Cloud,据说不仅可以实现异构数据实时同步,还永久 100% 免费,但就是不知道怎么获取.怎么用? 打开相关文档逐渐陷入迷茫,术语."黑话"随处可见, ...