dsu on tree 本质上是一个 启发式合并 复杂度 \(O(n\log n)\) 不支持修改 只能支持子树统计 不能支持链上统计…

先跑一遍树剖的dfs1

搞出来轻重儿子…

求每个节点的子树上有多少颜色为k的节点

有一个朴素的\(N^2\)暴力做法…

每个点来个\(dfs\) 没了

好您会dfs序 主席树/莫队 您赢了?但是并没有dsu on tree 好打。。

每次加入 \(x\) 点的时候 考虑暴力将 \(x\) 的子树放入 然后取出 消除贡献…

所以先统计轻儿子 然后再统计重儿子 最后消除轻儿子的贡献…

树剖让这棵树变成\(\log n\) 条链…

所以易证复杂度 \(n \log n\)

CF600E Lomsat gelral

// Isaunoya
#include<bits/stdc++.h>
using namespace std ;
using LL = long long ;
using uint = unsigned int ;
#define int long long
#define fir first
#define sec second
#define pb push_back
#define mp(x , y) make_pair(x , y)
template < typename T > inline void read(T & x) { x = 0 ; int f = 1 ; register char c = getchar() ;
for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
x *= f ;
}
template < typename T > inline void print(T x) {
if(! x) { putchar('0') ; return ; }
static int st[105] ;
if(x < 0) putchar('-') , x = -x ;
int tp = 0 ;
while(x) st[++ tp] = x % 10 , x /= 10 ;
while(tp) putchar(st[tp --] + '0') ;
}
template < typename T > inline void print(T x , char c) { print(x) ; putchar(c) ; }
template < typename T , typename ...Args > inline void read(T & x , Args & ...args) { read(x) ; read(args...) ; }
template < typename T > inline void sort( vector < T > & v) { sort(v.begin() , v.end()) ; return ; }
template < typename T > inline void unique( vector < T > & v) { sort(v) ; v.erase(unique(v.begin() , v.end()) , v.end()) ; }
template < typename T > inline void cmax(T & x , T y) { if(x < y) x = y ; return ; }
template < typename T > inline void cmin(T & x , T y) { if(x > y) x = y ; return ; }
const int Mod = LLONG_MAX ;
inline int QP(int x , int y) { int ans = 1 ;
for( ; y ; y >>= 1 , x = (x * x) % Mod)
if(y & 1) ans = (ans * x) % Mod ;
return ans ;
}
template < typename T > inline T gcd(T x , T y) { if(y == 0) return x ; return gcd(y , x % y) ; }
template < typename T > inline T lcm(T x , T y) { return x * y / gcd(x , y) ; }
template < typename T > inline void mul(T & x , T y) { x = 1LL * x * y ; if(x >= Mod) x %= Mod ; }
template < typename T > inline void add(T & x , T y) { if((x += y) >= Mod) x -= Mod ; }
template < typename T > inline void sub(T & x , T y) { if((x -= y) < 0) x += Mod ; }
const int N = 1e5 + 10 ;
int n ;
int co[N] , head[N] , cnt = 0 ; int son[N] , size[N] ;
int ctl[N] , num[N] , top = 0 ; int a[N] , sum[N] ;
struct node { int v , nxt ; } e[N << 1] ;
inline void add(int u , int v) { e[++ cnt].v = v ; e[cnt].nxt = head[u] ; head[u] = cnt ; return ; }
inline void dfs(int u , int fa) {
size[u] = 1 ;
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ;
if(v == fa) continue ;
dfs(v , u) ;
size[u] += size[v] ;
if(size[v] > size[son[u]]) son[u] = v ;
}
}
inline void upd(int u , int fa , int val) {
sum[num[co[u]]] -= co[u] ;
num[co[u]] += val ;
sum[num[co[u]]] += co[u] ;
if(sum[top + 1]) ++ top ;
if(! sum[top]) -- top ;
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ;
if(v != fa && ! ctl[v]) upd(v , u , val) ;
}
}
inline void dfs2(int u , int fa , int kep) {
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ;
if(v ^ fa && v ^ son[u]) dfs2(v , u , 0) ;
} if(son[u]) dfs2(son[u] , u , 1) , ctl[son[u]] = 1 ;
upd(u , fa , 1) ;
ctl[son[u]] = 0 ;
a[u] = sum[top] ;
if(! kep) upd(u , fa , -1) ;
}
signed main() {
read(n) ;
for(register int i = 1 ; i <= n ; i ++) read(co[i]) ;
for(register int i = 1 ; i <= n - 1 ; i ++) {
int u , v ; read(u , v) ;
add(u , v) ; add(v , u) ;
} dfs(1 , 0) ; dfs2(1 , 0 , 1) ;
for(register int i = 1 ; i <= n ; i ++) print(a[i] , ' ') ;
return 0 ;
}

CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

这题要求重排之后回文 即可以用二进制表示

\(2^i\) 次刚好可以表示…

而组成回文串的要求是最多一个出现奇数次的字符…

#include<bits/stdc++.h>
using namespace std ;
int n ;
const int N = 5e5 + 10 ;
struct node {
int v , nxt , w ;
} e[N] ;
int head[N] , cnt = 0 ;
inline void add(int u , int v , int w) {
e[++ cnt] = { v , head[u] , w } ;
head[u] = cnt ; return ;
}
int sz[N] , son[N] ,d[N] , dep[N] ;
inline void dfs(int u) { sz[u] = 1 ;
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ;
d[v] = d[u] ^ (1 << e[i].w) ; dep[v] = dep[u] + 1 ;
dfs(v) ; sz[u] += sz[v] ;
if(sz[v] > sz[son[u]]) son[u] = v ;
}
}
int mx = 0 , col[1 << 22] , ans[N] , now = 0 ;
inline void calc(int u) {
if(col[d[u]]) { mx = max(mx , col[d[u]] - now + dep[u]) ; }
for(register int i = 0 ; i < 22 ; i ++)
if(col[(1 << i) ^ d[u]]) mx = max(mx , dep[u] + col[(1 << i) ^ d[u]] - now) ;
}
inline void Calc(int u) { calc(u) ;
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ; Calc(v) ;
}
}
inline void up(int u) { col[d[u]] = max(dep[u] , col[d[u]]) ; }
inline void upd(int u) { up(u) ;
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ; upd(v) ;
}
}
inline void clear(int u) { col[d[u]] = 0 ;
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ;
clear(v) ;
}
}
inline void dfs(int u , bool kep) {
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ;
if(v ^ son[u]) dfs(v , 0) ;
}
if(son[u]) dfs(son[u] , 1) ;
now = dep[u] << 1 ;
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ;
mx = max(mx , ans[v]) ;
}
for(register int i = head[u] ; i ; i = e[i].nxt) {
int v = e[i].v ;
if(v == son[u]) continue ;
Calc(v) ; upd(v) ;
}
calc(u); up(u) ; ans[u] = mx ;
if(! kep) { clear(u) ; mx = 0 ; }
}
signed main() {
#ifdef _WIN64
freopen("0.in" , "r" , stdin) ;
#endif
ios :: sync_with_stdio(false) ;
cin.tie(nullptr) ;
cout.tie(nullptr) ;
cin >> n ;
for(register int i = 2 ; i <= n ; i ++) {
int x ; char c ;
cin >> x >> c ;
add(x , i , (c - 'a')) ;
}
dfs(1) ; dfs(1 , 1) ;
for(register int i = 1 ; i <= n ; i ++)
cout << ans[i] << ' ' ;
return 0 ;
}

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(树上启发式合并)

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

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

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

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

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

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

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

  7. 【学习笔记/题解】树上启发式合并/CF600E Lomsat gelral

    题目戳我 \(\text{Solution:}\) 树上启发式合并,是对普通暴力的一种优化. 考虑本题,最暴力的做法显然是暴力统计每一次的子树,为了避免其他子树影响,每次统计完子树都需要清空其信息. ...

  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.一般问题的解决不包含对树的修改,所有答案可以离线解决 算法思路:这类问题的特点在于父 ...

随机推荐

  1. qt creator源码全方面分析(2-1)

    目录 coding-style.html 提交代码 二进制兼容性和源代码兼容性 代码构造 格式化 利用标识符 空格 大括号 圆括号 换行符 声明 命名空间 模式与实践 命名空间 传递文件名 插件扩展点 ...

  2. 微服务之docker(二)

    一.SpringCloud/SpringBoot整合docker 使用docker的maven组建构建springboot应用(官方文档:https://spring.io/guides/gs/spr ...

  3. python中更人性化的一个单元测试框架:nose2

    如果你学过 python 进行自动化测试,你一定使用过 unittest.今天我们要讲的 nose2 是一个高级版本的 unittest.他比 unittest 更容易理解,用起来也更加方便一些. 快 ...

  4. Features for Multi-Target Multi-Camera Tracking and Re-identification论文解读

    解读一:Features for Multi-Target Multi-Camera Tracking and Re-identification Abstract MTMCT:从多个摄像头采集的视频 ...

  5. tomcat-windows10环境搭建

    1.进入Tomcat官网Apache Tomcat® - Welcome! 2.根据操作系统选择合适的版本下载 zip用于windows操作系统, tar.gz用于unix和linux操作系统 Bin ...

  6. Scala 学习(9)之「函数式编程」

    引用透明 对相同的输入,总是能得到相同的输出. 如果 f(x) 的参数 x 和函数体都是引用透明的,那么函数 f 是纯函数. 违反引用透明的例子 我们可以很清楚的看到,对于相同的输入,第二次调用app ...

  7. XXE漏洞复现步骤

    XXE漏洞复现步骤 0X00XXE注入定义 XXE注入,即XML External Entity,XML外部实体注入.通过 XML 实体,”SYSTEM”关键词导致 XML 解析器可以从本地文件或者远 ...

  8. Python原来这么好学-2.1节: 选择PyCharm作为开发工具

    这是一本教同学们彻底学通Python的高质量学习教程,认真地学习每一章节的内容,每天只需学好一节,帮助你成为一名卓越的Python程序员: 本教程面向的是零编程基础的同学,非科班人士,以及有一定编程水 ...

  9. 20200105--python学习数据类型总结

    总结 python中的数据类型:整型/布尔类型/字符串/元组/列表/字典/集合 注意:列表,字典,集合都不能作为字典中的key,也不能作为集合中的元素 数据类型: (1)整型 (2)布尔类型:只有两个 ...

  10. 卫星轨道相关笔记SGP4

    由卫星历书确定卫星轨道状态向量 卫星历书的表示方法有2种: TLE(Two Line Element),和轨道根数表示方法 由卫星历书计算出卫星轨道状态向量的方法有2种: SGP方法,NORAD的方法 ...