初涉DSU on tree
早先以为莫队是个顶有用的东西,不过好像树上莫队(不带修)被dsu碾压?
dsu one tree起源
dsu on tree是有人在cf上blog上首发的一种基于轻重链剖分的算法,然后好像由因为这个人后来在cf上办了场比赛出了道dsu on tree的裸题由此出名?
这个是原博客地址:http://codeforces.com/blog/entry/44351
大概思想就是一种树上启发式合并,利用轻重链剖分把重复计算的答案利用起来,从而把时间复杂度控制在$O(n log n)$(不过不能修改)。
注意dsu的一大特点:一次dsu之后是把整棵树的答案都处理出来,因此它更适合大量查询的情况。
算法流程
下面讲一下算法流程:
1.预处理树的结构,把$fa$,$son$,$tot$处理出来。具体操作参见树剖。
2.为了统计当前节点答案,先递归处理所有轻儿子。然后递归回当前节点时,其子树内除了重儿子都已经处理好答案了。
3.如果有重儿子,那么递归重儿子同时标记重儿子这个节点
4.现在子树信息都已经处理好了,考虑向上合并信息。我们把子树内所有点都统计颜色一遍(除了重儿子的家族)
5.现在子树信息传递好了
6.如果这个节点是轻儿子转移过来的,那么清除这颗子树所有信息(包括子树里的重儿子)
7.向上递归返回
void color(int x, int c) //把x的子树都统计一遍
{
cnt[a[x].col] += c;
if (c>&&mx <= cnt[a[x].col])
{
if (mx < cnt[a[x].col]) mx = cnt[a[x].col], sum = ;
sum += a[x].col; //如果现在是
}
for (int i=; i<f[x].size(); i++)
if (f[x][i]!=a[x].fa&&!vis[f[x][i]]) //处理儿子节点
color(f[x][i], c);
}
void dfs2(int x, bool fl) //dsu:x表示当前节点 fl表示当前节点是轻儿子还是重儿子
{
for (int i=; i<f[x].size(); i++)
if (f[x][i]!=a[x].son&&f[x][i]!=a[x].fa)
dfs2(f[x][i], );
if (a[x].son!=-) dfs2(a[x].son, ), vis[a[x].son] = ;
color(x, ); //c==1是统计答案;c==0是消除答案
ans[x] = sum;
if (a[x].son!=-) vis[a[x].son] = ;
if (!fl) color(x, -), mx = sum = ;
}
上面讲得似乎不适合初学者?……学的时候也看这些文字描述,不过还是有些疑惑。下面用一些图片来描述一下。
简单易懂的图片描述?

假设现在我们已经递归处理好了重儿子和轻儿子,现在打算向上传递到x节点。
我们的重儿子的颜色的信息是保存下来的,所以只需要再次添加轻儿子的颜色信息。
光光这样是不够的,可以想到如果出现这种情况呢?

现在x节点的颜色信息已经被确定了,并且在a统计时候信息并没有删除,好像这样会对b节点统计时候产生影响???
如果你这样想那么就跟我一样了。
注意到$dsu$的$dfs2$时候带了个$fl$参数表示当前节点是轻儿子还是重儿子。那么这个东西到底是干嘛用的呢?

这个a节点是由轻儿子转移过来的,因此它的$fl==0$。然而我们知道轻儿子是不保存信息的。
当a节点向上递归回去的时候,说明a节点的子树都已经统计好了。那么最后就有一个$color(x, -1)$操作,把x子树全部「清洗」掉去。
这样子按照轻重链剖分的顺序我们就能够利用大量重复的子树颜色信息,从而控制复杂度在$O(n log n)$了!
FAQ
这是是来自dalao的疑问:
x_o_r:上面那个算法流程第六步在干吗?
aq:这个是算法流程。
x_o_r:为什么要这么做?
aq:这个是流程,你首先假装它是对的。现在你哪里没看懂?
x_o_r:这个算法如何证明复杂度和正确性?
(x_o_r掉线)
XSamsara:为什么流程要先处理轻儿子再处理重儿子?不能反过来吗?
(x_o_r:轻重链剖分不就是暴力吗)
aq:因为这样快啊 这里为了统计这个节点,我们需要开一个桶。呃我知道这个“桶”的概念很抽象,就是理解成为一个颜色的hash数组,它的作用就是记录什么颜色有多少个。举个例子hash[x]=c就是表示x这个颜色有c个。诶如果想看复杂度证明的话,cptraser的博客写了个证明的。
XSamsara:轻重链有什么区别?为什么要区分当前节点是轻儿子还是重儿子?
(片刻)
XSamsara:哦,如果当前节点是轻儿子,最后擦除它是不是为了不影响其他子树的统计?但是为什么还要区分轻重链?是为了正确性还是为了效率?
aq:剖分轻重链的目的就是为了快。比方说我们先从重儿子开始做也可以啊。但是按照轻重儿子的顺序就能够把复杂度控制在$O(nlogn)$。
XSamsara:按照轻重儿子的顺序又会怎么样呢?为什么能够节省复杂度?
aq:考虑我们最最暴力的$O(n^2)$操作,统计完之后为了不影响其他子树统计,我们每一次都需要擦除整颗子树在桶里面的信息。这里,我们先做轻儿子并擦除,最后来统计重儿子,于是我们重儿子的信息就可以先不用擦了,因为当前节点也需要重儿子整颗子树的信息啊!
XSanasara:噢……原来轻重链剖分只是为了效率,我对它的期望太高了,以为是什么玄妙操作……
后续应用
1.dfs序不修改莫队的优秀替代品:$O(\sqrt n) -> O(log n)$
2.然后结合点分治可做一些有根树上的路径统计问题
似乎讲的很清晰易懂啊?
题目
Educational Codeforces Round 2 E Lomsat gelral
You are given a rooted tree with root in vertex 1. Each vertex is coloured in some colour.
Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the subtree of vertex v more times than colour c. So it's possible that two or more colours will be dominating in the subtree of some vertex.
The subtree of vertex v is the vertex v and all other vertices that contains vertex v in each path to the root.
For each vertex v find the sum of all dominating colours in the subtree of vertex v.
Input
The first line contains integer n (1 ≤ n ≤ 105) — the number of vertices in the tree.
The second line contains n integers ci (1 ≤ ci ≤ n), ci — the colour of the i-th vertex.
Each of the next n - 1 lines contains two integers xj, yj (1 ≤ xj, yj ≤ n) — the edge of the tree. The first vertex is the root of the tree.
Output
Print n integers — the sums of dominating colours for each vertex.
题目大意
就是询问所有子树中出现次数最多的颜色和。
题目分析
上面应该讲的很清楚了。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = ; struct node
{
int fa,tot,son;
ll col;
}a[maxn];
std::vector<int> f[maxn<<];
bool vis[maxn];
int n;
ll cnt[maxn],ans[maxn],mx,sum; void dfs1(int x, int fa)
{
a[x].fa = fa, a[x].son = -, a[x].tot = ;
for (int i=; i<f[x].size(); i++)
if (f[x][i]!=fa){
dfs1(f[x][i], x);
a[x].tot += a[f[x][i]].tot;
if (a[x].son ==-||a[a[x].son].tot < a[f[x][i]].tot)
a[x].son = f[x][i];
}
}
void color(int x, int c)
{
cnt[a[x].col] += c;
if (c>&&mx <= cnt[a[x].col])
{
if (mx < cnt[a[x].col]) mx = cnt[a[x].col], sum = ;
sum += a[x].col;
}
for (int i=; i<f[x].size(); i++)
if (f[x][i]!=a[x].fa&&!vis[f[x][i]])
color(f[x][i], c);
}
void dfs2(int x, bool fl)
{
for (int i=; i<f[x].size(); i++)
if (f[x][i]!=a[x].son&&f[x][i]!=a[x].fa)
dfs2(f[x][i], );
if (a[x].son!=-) dfs2(a[x].son, ), vis[a[x].son] = ;
color(x, );
ans[x] = sum;
if (a[x].son!=-) vis[a[x].son] = ;
if (!fl) color(x, -), mx = sum = ;
}
int main()
{
scanf("%d",&n);
for (int i=; i<=n; i++) scanf("%I64d",&a[i].col);
for (int i=; i<n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
f[x].push_back(y), f[y].push_back(x);
}
dfs1(, );
dfs2(, );
for (int i=; i<=n; i++) printf("%I64d ",ans[i]);
return ;
}
(注意一下要开long long)
END
初涉DSU on tree的更多相关文章
- CF 741D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths [dsu on tree 类似点分治]
D. Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths CF741D 题意: 一棵有根树,边上有字母a~v,求每个子树中最长的边,满 ...
- CF 570D. Tree Requests [dsu on tree]
传送门 题意: 一棵树,询问某棵子树指定深度的点能否构成回文 当然不用dsu on tree也可以做 dsu on tree的话,维护当前每一个深度每种字母出现次数和字母数,我直接用了二进制.... ...
- [dsu on tree]【学习笔记】
十几天前看到zyf2000发过关于这个的题目的Blog, 今天终于去学习了一下 Codeforces原文链接 dsu on tree 简介 我也不清楚dsu是什么的英文缩写... 就像是树上的启发式合 ...
- CF 375D. Tree and Queries【莫队 | dsu on tree】
题意: 一棵树,询问一个子树内出现次数$≥k$的颜色有几种 强制在线见上一道 用莫队不知道比分块高到哪里去了,超好写不用调7倍速度!!! 可以用分块维护出现次数这个权值,实现$O(1)-O(\sqrt ...
- dsu on tree 树上启发式合并 学习笔记
近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...
- UOJ#266. 【清华集训2016】Alice和Bob又在玩游戏 博弈,DSU on Tree,Trie
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ266.html 题解 首先我们可以直接暴力 $O(n^2)$ 用 sg 函数来算答案. 对于一个树就是枚举 ...
- dsu on tree入门
先瞎扯几句 说起来我跟这个算法好像还有很深的渊源呢qwq.当时在学业水平考试的考场上,题目都做完了不会做,于是开始xjb出题.突然我想到这么一个题 看起来好像很可做的样子,然而直到考试完我都只想出来一 ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- [Codeforces741D]Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths——dsu on tree
题目链接: Codeforces741D 题目大意:给出一棵树,根为$1$,每条边有一个$a-v$的小写字母,求每个点子树中的一条最长的简单路径使得这条路径上的边上的字母重排后是一个回文串. 显然如果 ...
随机推荐
- 百度网盘不限速!VIP视频免费看!这两款插件被无数人安利!
今天给给位推荐两款,我一直在使用的浏览器插件,简直爆炸!全网VIP视频随意看,所有网页上的视频,你想要的全部都能下载! 这两款插件堪称日常必备插件,只要你使用浏览器,就一定需要下面这些插件功能:快速下 ...
- 在项目中移除CocoaPods
如果你觉得CocoaPods让你的项目出现了问题,不好用甚至是恶心,想将其从项目中彻底移除,也有方法: 1.删除工程文件夹下的Podfile.Podfile.lock和Pods文件夹. 2.删除xcw ...
- [題解]luogu_P1052 過河
來源:題解 不發題面 因為 l 範圍太大,而石子數卻很少,步數也僅僅在1~10之間, 也就是說兩個石子之間很有可能間隔很大的距離,不管怎麼跳都能跳過去,那麼中間那些怎麼樣都能跳過去的區間和沒有等價, ...
- 解决XP“不是有效Win32程序” 不是改Platform toolset
背景 最近在写一个窗口程序,想在Windows XP上也能跑.先用vs 2015的App Wizard生成了一个实例窗口程序,按照网上大部分攻略,将 "Properties - Genera ...
- Oracle / PLSQL写语句 常用的几个函数
下面开始记录一下,自己在Oracle或者PLSQL常用的几个函数, 1add_months 增加或减去月份2. last_day(sysdate) 返回日期的最后一天3. months_between ...
- base64 正则表达式 ,判断图片是base64还是图片链接
base64正则表达式 在这里看到https://segmentfault.com/q/1010000009628242/a-1020000009629647 var reg = /^\s*data: ...
- php中3DES加密技术
因为工作中要用到加密,接口中要求也是用密文传输数据,用到3des加密,就研究了一下. 在网上也找了好多,但是都不可以用,没法正式运行,终于找到一个可以运行的,自己又修改了一下,记录下来,以后还可能会用 ...
- 当获取相似数据时,使用不同方法调用不同sp,但是使用同一个方法去用IIDataReader或者SqlDataReader读取数据时需要判断column name是否存在。
/// <summary> /// Checks clumn Name /// </summary> /// <param name="reader" ...
- 用户登陆界面(jquery)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- rpm包的安装方法
每次都记不住,这次必须整理一下!cd到你存放rpm文件包的目录rpm -ivh linuxqq-v1.0.2-beta1.i386.rpm安装时可能会遇到缺少包的错误.rpm -ivh --repla ...