dsu on tree[树上启发式合并学习笔记]
dsu on tree 本质上是一个 启发式合并 复杂度 \(O(n\log n)\) 不支持修改 只能支持子树统计 不能支持链上统计…
先跑一遍树剖的dfs1
搞出来轻重儿子…
求每个节点的子树上有多少颜色为k的节点
有一个朴素的\(N^2\)暴力做法…
每个点来个\(dfs\) 没了
好您会dfs序 主席树/莫队 您赢了?但是并没有dsu on tree 好打。。
每次加入 \(x\) 点的时候 考虑暴力将 \(x\) 的子树放入 然后取出 消除贡献…
所以先统计轻儿子 然后再统计重儿子 最后消除轻儿子的贡献…
树剖让这棵树变成\(\log n\) 条链…
所以易证复杂度 \(n \log n\)
// 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[树上启发式合并学习笔记]的更多相关文章
- dsu on tree 树上启发式合并 学习笔记
近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...
- dsu on tree (树上启发式合并) 详解
一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...
- dsu on tree(树上启发式合并)
简介 对于一颗静态树,O(nlogn)时间内处理子树的统计问题.是一种优雅的暴力. 算法思想 很显然,朴素做法下,对于每颗子树对其进行统计的时间复杂度是平方级别的.考虑对树进行一个重链剖分.虽然都基于 ...
- 树上启发式合并(dsu on tree)学习笔记
有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...
- 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)
(这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...
- 神奇的树上启发式合并 (dsu on tree)
参考资料 https://www.cnblogs.com/zhoushuyu/p/9069164.html https://www.cnblogs.com/candy99/p/dsuontree.ht ...
- 【学习笔记/题解】树上启发式合并/CF600E Lomsat gelral
题目戳我 \(\text{Solution:}\) 树上启发式合并,是对普通暴力的一种优化. 考虑本题,最暴力的做法显然是暴力统计每一次的子树,为了避免其他子树影响,每次统计完子树都需要清空其信息. ...
- CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 树上启发式合并(DSU ON TREE)
题目描述 一棵根为\(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种). 一条简单路径被称为\(Dokhtar-kosh\)当且仅当路径上的字符经过重新排序后可以变成一个回文串. 求 ...
- 树上启发式合并(dsu on tree)
树上启发式合并属于暴力的优化,复杂度O(nlogn) 主要解决的问题特点在于: 1.对于树上的某些信息进行查询 2.一般问题的解决不包含对树的修改,所有答案可以离线解决 算法思路:这类问题的特点在于父 ...
随机推荐
- uml-类图书写指南
说明 类图是最常用的UML图,面向对象建模的主要组成部分.它用于描述系统的结构化设计,显示出类.接口以及它们之间的静态结构和关系. 类图主要产出于面向对象设计的分析和设计阶段,用来描述系统的静态结构. ...
- Win2008 远程时提示"要登录到此远程计算机,您必须被授予允许通过终端登录登录的权限"的解决方法
问题描述 ECS Windows 2008 远程登陆时提示"要登录到此远程计算机,您必须被授予允许通过终端登录登录的权限",如下图所示: 问题分析 组策略中做了设置不允许管理员组成 ...
- Linux下静态ip的配置
------------恢复内容开始------------ TYPE=Ethernet BOOTPROTO=static#dhcp改为static,采用静态方式 DEFROUTE=yes IPV4_ ...
- python书籍推荐以及杂七杂八
黄哥推荐学习Python 10本好书 Python3网络爬虫开发实战 github daily-reading 想对大家说的话 POJ 算法练习 http://openjudge.cn/ http:/ ...
- CCF_201612-2_火车购票
http://115.28.138.223/view.page?gpid=T46 水. #include<iostream> #include<cstring> #includ ...
- CCF_ 201509-3_模板生成系统
又是一道考验细心和耐心的题,不知道哪里出问题了,一直只有90分 = =! #include<cstdio> #include<iostream> #include<cst ...
- 【5min+】 巨大的争议?C# 8 中的接口
系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...
- num04---模板方法模式
最近看书又遇到模板方法模式,具体是在同步器(AQS)的内容上.就顺便再来回顾下. 同步器AbstractQueuedSynchronizer(AQS)是一个抽象类.其中定义了 基本 ...
- win10系统下安装JDK1.8及配置环境变量的方法
本次演示基于windows10操作系统,如果你是linux,请参考:https://www.yn2333.com/archives/linux上安装JDK8 1:下载安装包 地址:https://ww ...
- Ubuntu下LAMP的环境配置教程
总体来说,Ubuntu下安装LAMP环境是比较简单的,只需按照命令行执行即可,记录操作以备不时之需. 一,首先更新Ubuntu里面所有的软件 sudo apt-get update 二.之后安装Apa ...