dsu on tree总结
dsu on tree
树上启发式合并。我并不知道为什么要叫做这个名字。。。
干什么的
可以在\(O(n\log n)\)的时间内完成对子树信息的询问,可横向对比把树按\(dfs\)序转成序列问题的\(O(n\sqrt n)\)莫队算法。
怎么实现
当\(dfs\)到一个点\(u\),执行以下操作:
1、递归处理所有轻儿子;
2、递归处理重儿子;
3、计算整棵子树的贡献(在第2步中重儿子的贡献得以保留,所以不需要重复计算);
4、若点\(u\)不是其父亲的重儿子,删除整棵子树的贡献。
看上去像是暴力?
复杂度分析
只有当存在一条轻边时,这条轻边所连接的子树才需要被重新计算一次贡献。那么一个点被重复计算的次数就等于这个点到根路径上经过的轻边条数。而根据轻重链剖分那套理论,一个点到根路径上的轻边不会超过\(\log n\)条,所以这个算法的复杂度为\(O(n\log n*t)\),其中\(t\)为一次计算贡献的复杂度。
题目
懒得分开写博客了qaq
CF600E Lomsat gelral
luogu
一棵树有\(n\)个结点,每个结点都有一种颜色,求每个子树中出现次数最多的颜色(们)的编号之和。
sol:维护\(num_i\)表示有多少种颜色出现了\(i\)次,\(sum_i\)表示这些颜色的编号和。显然计算贡献是\(O(1)\)的。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
#define ll long long
const int N = 1e5+5;
int n,a[N],to[N<<1],nxt[N<<1],head[N],cnt;
int sz[N],son[N],vis[N],tot[N],num[N],Max;ll sum[N],ans[N];
void link(int u,int v){
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void dfs1(int u,int f){
sz[u]=1;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f){
dfs1(to[e],u);sz[u]+=sz[to[e]];
if (sz[to[e]]>sz[son[u]]) son[u]=to[e];
}
}
void update(int u,int f,int val){
int &t=tot[a[u]];
--num[t];sum[t]-=a[u];
t+=val;
++num[t];sum[t]+=a[u];
if (val>0) Max=max(Max,t);
else if (!num[Max]) --Max;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f&&!vis[to[e]])
update(to[e],u,val);
}
void dfs(int u,int f,int keep){
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f&&to[e]!=son[u])
dfs(to[e],u,0);
if (son[u]) dfs(son[u],u,1),vis[son[u]]=1;
update(u,f,1);
ans[u]=sum[Max];
vis[son[u]]=0;
if (!keep) update(u,f,-1);
}
int main(){
n=gi();
for (int i=1;i<=n;++i) a[i]=gi();
for (int i=1;i<n;++i){
int u=gi(),v=gi();
link(u,v);link(v,u);
}
dfs1(1,0);dfs(1,0,1);
for (int i=1;i<=n;++i) printf("%I64d ",ans[i]);
puts("");return 0;
}
CF570D Tree Requests
luogu
给你一棵\(n\)个节点的树,每个节点上有一个小写英文字母。每次询问\(v_i,h_i\),表示在\(v_i\)子树中所有深度为\(h_i\)的点上的字母拿出来重新组合能否形成一个回文串。
sol:一些字母重新组合后能够形成回文串当且仅当存在至多一个字母的出现次数为奇数。所以可以把每个小写英文字母当作是一个二进制位然后维护子树中每个深度的异或和即可。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 5e5+5;
struct node{int h,nxt;}a[N];
int n,m,nxt[N],head[N],val[N],ft[N];char s[N];
int dep[N],sz[N],son[N],vis[N],sum[N],ans[N];
void add(int v,int h,int id){
a[id]=(node){h,ft[v]};ft[v]=id;
}
void dfs1(int u,int f){
dep[u]=dep[f]+1;sz[u]=1;
for (int v=head[u];v;v=nxt[v]){
dfs1(v,u),sz[u]+=sz[v];
if (sz[v]>sz[son[u]]) son[u]=v;
}
}
void update(int u){
sum[dep[u]]^=val[u];
for (int v=head[u];v;v=nxt[v])
if (!vis[v]) update(v);
}
bool cal(int x){
int res=0;
while (x) ++res,x^=x&-x;
return res<=1;
}
void dfs(int u,int f,int keep){
for (int v=head[u];v;v=nxt[v])
if (v!=son[u]) dfs(v,u,0);
if (son[u]) dfs(son[u],u,1),vis[son[u]]=1;
update(u);
for (int i=ft[u];i;i=a[i].nxt) ans[i]=cal(sum[a[i].h]);
vis[son[u]]=0;
if (!keep) update(u);
}
int main(){
n=gi();m=gi();
for (int i=2,f;i<=n;++i)
f=gi(),nxt[i]=head[f],head[f]=i;
scanf("%s",s+1);
for (int i=1;i<=n;++i) val[i]=1<<s[i]-'a';
for (int i=1,v,h;i<=m;++i) v=gi(),h=gi(),add(v,h,i);
dfs1(1,0);dfs(1,0,1);
for (int i=1;i<=m;++i) puts(ans[i]?"Yes":"No");
return 0;
}
CF208E Blood Cousins
luogu
给你一片森林,每次询问和一个点有相同的\(k\)次祖先的点有多少个。
sol:倍增跳到那个祖先上然后就是只需要知道该深度有多少个点就行了。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e5+5;
int n,m,nxt[N],head[N],root[N],fa[18][N],ft[N];
int dep[N],sz[N],son[N],vis[N],tot[N],ans[N];
struct node{int d,nxt;}a[N];
void add(int x,int d,int id){
a[id]=(node){d,ft[x]};ft[x]=id;
}
void dfs1(int u,int f){
fa[0][u]=f;dep[u]=dep[f]+1;sz[u]=1;
for (int i=1;i<=17;++i) fa[i][u]=fa[i-1][fa[i-1][u]];
for (int v=head[u];v;v=nxt[v]){
dfs1(v,u);sz[u]+=sz[v];
if (sz[v]>sz[son[u]]) son[u]=v;
}
}
void update(int u,int val){
tot[dep[u]]+=val;
for (int v=head[u];v;v=nxt[v])
if (!vis[v]) update(v,val);
}
void dfs(int u,int keep){
for (int v=head[u];v;v=nxt[v])
if (v!=son[u]) dfs(v,0);
if (son[u]) dfs(son[u],1),vis[son[u]]=1;
update(u,1);
for (int i=ft[u];i;i=a[i].nxt) ans[i]=tot[a[i].d]-1;
vis[son[u]]=0;
if (!keep) update(u,-1);
}
int main(){
n=gi();
for (int i=1;i<=n;++i){
int f=gi();
if (!f) root[++root[0]]=i;
else nxt[i]=head[f],head[f]=i;
}
for (int i=1;i<=root[0];++i) dfs1(root[i],0);
m=gi();
for (int i=1;i<=m;++i){
int x=gi(),k=gi(),d=dep[x];
for (int j=0;j<=17;++j) if (k&(1<<j)) x=fa[j][x];
add(x,d,i);
}
for (int i=1;i<=root[0];++i) dfs(root[i],0);
for (int i=1;i<=m;++i) printf("%d ",ans[i]);
puts("");return 0;
}
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$的小写字母,求每个点子树中的一条最长的简单路径使得这条路径上的边上的字母重排后是一个回文串. 显然如果 ...
- BZOJ.4182.Shopping(点分治/dsu on tree 树形依赖背包 多重背包 单调队列)
BZOJ 题目的限制即:给定一棵树,只能任选一个连通块然后做背包,且每个点上的物品至少取一个.求花费为\(m\)时最大价值. 令\(f[i][j]\)表示在点\(i\),已用体积为\(j\)的最大价值 ...
随机推荐
- Autorelease 性能测试
__weak NSString *string_weak_ = nil; - (void)viewDidLoad { [super viewDidLoad]; // 场景 1 NSString *st ...
- Graph I - Graph
Graph There are two standard ways to represent a graph G=(V,E)G=(V,E), where VV is a set of vertices ...
- wfst讲解
一.比较好的博客 1.0 官方网站 1.1 语音识别解码器(1)—自动机与半环 1.2 走进语音识别中的WFST 1.3Kaldi WFST 构图 学习 二.比较好的paper 三.开源项目 3.1 ...
- 一种基于openflow的虚拟化层软件flowvisor的API测试
注明:本文并不对openflow进行分析,本人也是略略知道这个概念,对flowvisor也只是对其API有所测试,更深的源码并未涉及,只是希望该文能对以后的flowvisor研究者提供些许帮助. 一: ...
- Linux Ubuntu安装sogou中文输入法
在linux下开发,有时还是需要使用中文输入法的,每次安装的时候都觉得痛苦,这次做下记录,方便下次安装. 安装fcitx 安装sogou输入法之前,需要安装fcitx(Free Chinese Inp ...
- Selenium封装
import os from selenium import webdriver from selenium.webdriver.common.by import By from selenium.w ...
- JS模拟Dictionary
function Map() { this.keys = new Array(); this.data = new Array(); //添加键值对 this.set = function (key, ...
- Node.js 上传图片并保存
Node.js 上传图片并保存 依赖 package.json 文件 { "name": "demo", "version": " ...
- Oracle与MySQL使用区别
与MySQL通过创建不同的数据库来存储表 Oracle提出表空间(tablespace)的概念作为逻辑上的存储区域来存储表, 而不同的表空间由不同的用户来管理 用户可以授予权限或角色 举例: 使用PL ...
- iOS:多媒体(18-01-25更)
1.音频 2.视频 1. 2.AVPlayer 1.音频 2.视频 1. 2.AVPlayer 0).写在前面 AVPlayer 主要包含 AVPlayer.AVPlayerItem.AVPlayer ...