树上启发式合并 (dsu on tree)
这个故事告诉我们,在做一个辣鸡出题人的比赛之前,最好先看看他发明了什么新姿势= =居然直接出了道裸题
参考链接:
http://codeforces.com/blog/entry/44351(原文)
http://blog.csdn.net/QAQ__QAQ/article/details/53455462
这种技巧可以在O(nlogn)的时间内解决绝大多数的无修改子树询问问题。
例1 子树颜色统计
有一棵n个点的有根树,根为1,每个点有一个1~n的颜色,对于每一个点给了一个数k,要询问这个子树中颜色为k的点的个数。n<=500000。
这个例子当然过于trivial,dfs序完一棵主席树就能水过去,不过这不是重点...
我们的目标就是实现一个不那么暴力的东西,可以代替以下代码:
Edg int n,cc[SZ],col[SZ],ks[SZ],anss[SZ];
void edt(int x,int f,int v)
{
cc[col[x]]+=v;
for esb(x,e,b)
if(b!=f) edt(b,x,v);
}
void dfs(int x,int f=0)
{
edt(x,f,1);
anss[x]=cc[ks[x]];
edt(x,f,-1);
for esb(x,e,b)
if(b!=f) dfs(b,x);
}
输入大致如下:
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",col+i);
for(int i=1;i<=n;i++) scanf("%d",ks+i);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
adde(a,b);
}
dfs(1);
for(int i=1;i<=n;i++) printf("%d\n",anss[i]);
}
以下是一个清真的nlogn做法:
Edg
int n,cc[SZ],col[SZ],ks[SZ],anss[SZ];
int sz[SZ],son[SZ];
void gs(int x,int f=0)
{
sz[x]=1;
for esb(x,e,b)
{
if(b==f) continue;
gs(b,x); sz[x]+=sz[b];
if(sz[b]>sz[son[x]]) son[x]=b;
}
}
int skip=0;
void edt(int x,int f,int v)
{
cc[col[x]]+=v;
for esb(x,e,b)
if(b!=f&&b!=skip) edt(b,x,v);
}
void dfs(int x,int f=0,bool kep=0)
{
for esb(x,e,b)
if(b!=f&&b!=son[x]) dfs(b,x);
if(son[x])
dfs(son[x],x,1), skip=son[x];
edt(x,f,1);
anss[x]=cc[ks[x]];
skip=0;
if(!kep) edt(x,f,-1);
}
(如果操作比较复杂的话建议看看下面的例三代码= =)
这为什么是nlogn的?因为一条重链会使所在子树大小翻一倍。
我们发现这个技巧十分好用,只要兹磁往一个集合里插入是O(1)的,删除元素到空为止是每个O(1)的(注意到这样写的话每次删除是一定会删到全空的),那么对于子树集合的询问就可以做到O(nlogn),不知道比辣鸡莫队高到哪里去了(莫队复杂度nsqrt(n),而且必须也要支持删除O(1))。
例2 Lomsat gelral(cf600E)
n个点的有根树,以1为根,每个点有一种颜色。我们称一种颜色占领了一个子树当且仅当没有其他颜色在这个子树中出现得比它多。求占领每个子树的所有颜色之和。
模板题啦。
#define SZ 666666
Edg
int n,cc[SZ],col[SZ],sz[SZ],son[SZ];
ll anss[SZ];
void gs(int x,int f=0)
{
sz[x]=1;
for esb(x,e,b)
{
if(b==f) continue;
gs(b,x); sz[x]+=sz[b];
if(sz[b]>sz[son[x]]) son[x]=b;
}
}
bool skip[SZ];
int cx=0; ll sum=0;
void edt(int x,int f,int k)
{
cc[col[x]]+=k;
if(k>0&&cc[col[x]]>=cx)
{
if(cc[col[x]]>cx)
sum=0, cx=cc[col[x]];
sum+=col[x];
}
for esb(x,e,b)
if(b!=f&&!skip[b]) edt(b,x,k);
}
void dfs(int x,int f=0,bool kep=0)
{
for esb(x,e,b)
if(b!=f&&b!=son[x]) dfs(b,x);
if(son[x])
dfs(son[x],x,1), skip[son[x]]=1;
edt(x,f,1);
anss[x]=sum;
if(son[x]) skip[son[x]]=0;
if(!kep)
edt(x,f,-1), cx=sum=0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",col+i);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
adde(a,b);
}
gs(1); dfs(1);
for(int i=1;i<=n;i++) printf("%I64d ",anss[i]);
}
例3 Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths(CF741D)
辣鸡出题人还我rating
我们称一个字符串为doubi string当且仅当重排它的字符可以组成一个回文串。
给出一个n个点的有根树,根为1,每条边上有一个字符(只有a~v,别问我为什么),求每个点的子树中所有简单路径可以组成的doubi string中的最长长度。
doubi string显然就是只有0/1个字符出现奇数次的字符串,如果只有a~v的话考虑把每个字符当做一个二进制位,把一个点i到根的路径异或值记为s[i],那么我们就是要对于每个x在子树中找到a和b,使得s[a]^s[b]为0或2的次幂,且dep[a]+dep[b]-dep[lca]*2最大。
那么问题来了,lca如果直接当做x算出来的答案是会变大的...看起来我们需要把这个东西扩展一下,让它只统计不同子树的。这个好办,对于每棵子树先统计再更新就行了。
模板大法好!
#define SZ 1234567
Edgc
int n,sz[SZ],son[SZ],fc[SZ],dep[SZ];
void gs(int x,int f=0)
{
sz[x]=1;
for esb(x,e,b)
{
if(b==f) continue;
fc[b]=fc[x]^vc[e];
dep[b]=dep[x]+1;
gs(b,x); sz[x]+=sz[b];
if(sz[b]>sz[son[x]]) son[x]=b;
}
}
const int S='v'-'a'+1,inf=1e9;
int md[5555555],cans,skip,cdep;
void clr(int x) {md[fc[x]]=-inf;}
void upd(int x)
{
cans=max(cans,md[fc[x]]+dep[x]-cdep*2);
for(int i=0;i<S;i++)
cans=max(cans,md[fc[x]^(1<<i)]+dep[x]-cdep*2);
}
void ins(int x)
{md[fc[x]]=max(md[fc[x]],dep[x]);}
template<void(*func)(int)>
void edt(int x,int f)
{
func(x);
for esb(x,e,b)
if(b!=f&&b!=skip) edt<func>(b,x);
}
int dp[SZ];
void dfs(int x,int f=0,bool kep=0)
{
for esb(x,e,b)
if(b!=f&&b!=son[x]) dfs(b,x);
if(son[x])
dfs(son[x],x,1), skip=son[x];
cdep=dep[x];
for esb(x,e,b) if(b!=f)
dp[x]=max(dp[x],dp[b]);
for esb(x,e,b) if(b!=f&&b!=son[x])
edt<upd>(b,x), edt<ins>(b,x);
upd(x); ins(x);
dp[x]=max(dp[x],cans);
skip=0;
if(!kep) edt<clr>(x,f), cans=-inf;
}
int main()
{
for(int i=0;i<(1<<S);i++)
md[i]=-inf;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
int x; char c[3];
scanf("%d%s",&x,c);
adde(i,x,1<<(c[0]-'a'));
}
gs(1); dfs(1);
for(int i=1;i<=n;i++)
printf("%d ",dp[i]);
}
树上启发式合并 (dsu on tree)的更多相关文章
- 神奇的树上启发式合并 (dsu on tree)
参考资料 https://www.cnblogs.com/zhoushuyu/p/9069164.html https://www.cnblogs.com/candy99/p/dsuontree.ht ...
- 【CF600E】Lomset gelral 题解(树上启发式合并)
题目链接 题目大意:给出一颗含有$n$个结点的树,每个节点有一个颜色.求树中每个子树最多的颜色的编号和. ------------------------- 树上启发式合并(dsu on tree). ...
- dsu on tree 树上启发式合并 学习笔记
近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...
- 树上启发式合并(dsu on tree)学习笔记
有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...
- dsu on tree (树上启发式合并) 详解
一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...
- 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)
(这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...
- 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.一般问题的解决不包含对树的修改,所有答案可以离线解决 算法思路:这类问题的特点在于父 ...
- hdu6191(树上启发式合并)
hdu6191 题意 给你一棵带点权的树,每次查询 \(u\) 和 \(x\) ,求以 \(u\) 为根结点的子树上的结点与 \(x\) 异或后最大的结果. 分析 看到子树,直接上树上启发式合并,看到 ...
随机推荐
- TokuDB存储引擎
TokuDB是Tokutek公司开发的基于ft-index(Fractal Tree Index)键值对的存储引擎. 它使用索引加快查询速度,具有高扩展性,并支持hot scheme modifica ...
- 原生js之四步走搞定Ajax
说到Ajax,不得不先提一下HTTP(HTTP,HyperText Transfer Protocol)协议,中文名:超文本传输协议,是互联网上应用最为广泛的一种网络协议.所有的WWW文件 ...
- YYModel 源码解读(二)之YYClassInfo.h (1)
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END 为了兼容Swift 中的 ? 和 ! oc 在6.3引入了两个新的类型注释:__nullable和__non ...
- linux内核调试技术之printk
原创博客:欢迎转载,转载请注明出处https://i.cnblogs.com/EditPosts.aspx?postid=6218383 1.简介(基于s3c2440 linux) 在内核调试技术之中 ...
- BFC的形成条件和特性分析
初学CSS时,我们学到很多有意思的CSS规则,比如外边距塌陷,还有浮动元素的一些特性等,其实这些规则背后都是BFC这个东西在控制,下面我们来看下BFC到底是什么. 什么是BFC BFC(Block f ...
- Android GradientDrawable(shape标签定义) 静态使用和动态使用(圆角,渐变实现)
Android GradientDrawable使用优势: 1. 快速实现一些基本图形(线,矩形,圆,椭圆,圆环) 2. 快速实现一些圆角,渐变,阴影等效果 3. 代替图片设置为View的背景 4. ...
- 配置Chrome支持本地(file协议)的AJAX请求
什么问题 WEB开发过程中,很多时候我们都是写一些简单的Demo,并不是开发一个完整项目,此时我们常见的操作是: 新建文件夹 新建需要的文件 在Sublime(或其他编辑器)中完成DEMO的编码 双击 ...
- [未完成]scikit-learn一般实例之九:用于随机投影嵌入的Johnson–Lindenstrauss lemma边界
Johnson–Lindenstrauss 引理表明任何高维数据集均可以被随机投影到一个较低维度的欧氏空间,同时可以控制pairwise距离的失真. 理论边界 由一个随机投影P所引入的失真是确定的,这 ...
- deb包的安装及dpkg命令小结
DPKG commands There are two actions, they are dpkg-query and dpkg-deb. Install a package # sudo dpkg ...
- Keil> 编译器特有的功能 > 关键字和运算符 > __weak
__weak 此关键字指示编译器弱导出符号. 可以将 __weak 关键字应用于函数和变量声明以及函数定义. 用法 函数和变量声明 对于声明,此存储类指定一个 extern 对象声明,即使不存在,也不 ...