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. uml-类图书写指南

    说明 类图是最常用的UML图,面向对象建模的主要组成部分.它用于描述系统的结构化设计,显示出类.接口以及它们之间的静态结构和关系. 类图主要产出于面向对象设计的分析和设计阶段,用来描述系统的静态结构. ...

  2. 情人节到了,Postman 都脱单了,那你咧?

    前言 Postman 是一款API接口调试工具,做过 Web 接口或多或少应该接触过. 通过它可以完成 Http 接口的调试,测试同学也可以基于此做一些自动化测试.另外 Postman 还提供其他高级 ...

  3. 高软期末考试 B2C模式

    一.软件工程知识点 简要总结 1.软件基础知识 瀑布模型: 我感觉整个<软件工程>书的布局就是按照瀑布模型来的,上面右图少个运维. 2.UML图 2.1 用例图 UseCase Diagr ...

  4. STM32片外SRAM作运行内存

    本例演示用的软硬件: 片内外设驱动库:STM32CubeF41.24.1的HAL库1.7.6,2019年4月12日 IDE:MDK-ARM 5.28.0.0,2019年5月 开发板:片外SRAM挂在F ...

  5. 基于 Google-S2 的地理相册服务实现及应用

    马蜂窝技术原创内容,更多干货请关注公众号:mfwtech 随着智能手机存储容量的增大,以及相册备份技术的普及,我们可以随时随地用手机影像记录生活,在手机中存储几千张甚至上万张照片已经是很常见的事情.但 ...

  6. Linux学习2-云服务器上安装java和tomcat环境

    在linux上部署java的项目,首先要安装JDK和Tomcat,具体要求怎么操作呢,我们一起来学习吧! JDK的安装步骤如下: 1.首先我们从官网下载jdk-8u231-linux-x64.rpm安 ...

  7. JumpServer部署与管理

    一.JumpServer 堡垒机概述 JumpServer由Python/Django进行开发.使用GNU GPL v2.0开源协议.也是全球首款完全开源的堡垒机.同时配备了业界领先的Web Term ...

  8. EIP

    EIP中的值就是CPU下次要执行的地址 jmp 直接修改eip的值 1.jmp imm=mov eip,imm 2.jmp r 3.jmp m call 直接修改eip的值,并把当前指令的下一行地址存 ...

  9. create-react-app 打包后静态文件过大 webpack优化

    在最近的项目里,页面和静态文件并不是很多的情况下,打包后发现产出的静态资源却很大. 1.关掉sourcemap 在config/webpack.config.js文件里,大概30几行的位置添加这样一句 ...

  10. golang的timer一些坑

    本文代码部分基于dive-to-gosync-workshop的代码 Golang 的NewTimer方法调用后,生成的timer会放入最小堆,一个后台goroutine会扫描这个堆,将到时的time ...