[CF1009F] Dominant Indices (+dsu on tree详解)
这道题用到了dsu(Disjoint Set Union) on tree,树上启发式合并。
先看了CF的官方英文题解,又看了看zwz大佬的题解,差不多理解了dsu on tree的算法。
但是时间复杂度有点玄学,问了一下zwz大佬才懂了为什么是nlogn。
先考虑暴力n^2的算法。
显然对于某个点,搜一遍它的子树,就能得到这个点的答案。这一步是O(n)的。
每个点都这么搞一遍,就是O(n^2)的暴力做法。
但是这个暴力做法有一点不足,子节点的答案没有应用到父节点的计算中,白白浪费时间重算一遍。
考虑优化,类似树链剖分,找出子树最大的儿子称作重儿子,把它的答案留着,这样计算父节点时就不用搜这个重儿子了。
显然保留子树最大的儿子的信息,能够节约最多的时间。
但是如果计算完重儿子的答案,保留了信息,再计算别的儿子的答案,已保留的信息会对当前的计算产生干扰。
所以我们先计算轻儿子,最后计算重儿子。
如果是轻儿子,更新答案计算后,暴力再改回去。
如果是重儿子,就留着。
计算完所有儿子的答案后,最后计算当前点。
只需要加上轻儿子的信息就好。重儿子的信息已经留着了,不用再加了。
下面是zwz大佬对于dsu on tree时间复杂度的证明:
每个节点只会在祖先节点的计算中被搜到。
而且只有它到它父亲是轻边的时候才会搜一遍。
所以每个点的计算次数是它到根的轻边数量,为logn。
所以总时间复杂度是nlogn。
感觉dsu也是挺暴力的,每次留一个,居然时间上优化了很多。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define MAXN 1000005
using namespace std; int n;
int hd[MAXN],nx[MAXN<<],to[MAXN<<],ec;
int dep[MAXN],sz[MAXN],ans[MAXN],cnt[MAXN]; void edge(int af,int at)
{
to[++ec]=at;
nx[ec]=hd[af];
hd[af]=ec;
} void pre(int p,int fa)
{
sz[p]=;
dep[p]=dep[fa]+;
for(int i=hd[p];i;i=nx[i])
{
if(to[i]==fa)continue;
pre(to[i],p);
sz[p]+=sz[to[i]];
}
} struct data
{
int d,v;
friend bool operator<(data q,data w)
{
if(q.v==w.v)return q.d>w.d;
return q.v<w.v;
}
}; priority_queue<data>qq; void add(int p,int fa)
{
cnt[dep[p]]++;
data neo={dep[p],cnt[dep[p]]};
qq.push(neo);
for(int i=hd[p];i;i=nx[i])if(to[i]!=fa)add(to[i],p);
} void del(int p,int fa)
{
cnt[dep[p]]--;
for(int i=hd[p];i;i=nx[i])if(to[i]!=fa)del(to[i],p);
} void dfs(int p,int fa,int stay)
{
int son=,mx=-;
for(int i=hd[p];i;i=nx[i])
{
if(to[i]==fa)continue;
if(mx<sz[to[i]])mx=sz[to[i]],son=to[i];
}
for(int i=hd[p];i;i=nx[i])
{
if(to[i]==fa||to[i]==son)continue;
dfs(to[i],p,);
}
if(son)dfs(son,p,);
for(int i=hd[p];i;i=nx[i])
{
if(to[i]==fa||to[i]==son)continue;
add(to[i],p);
}
cnt[dep[p]]++;
data neo={dep[p],cnt[dep[p]]};
qq.push(neo);
ans[p]=qq.top().d-dep[p];
if(!stay)
{
del(p,fa);
while(!qq.empty())qq.pop();
}
} int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
edge(x,y);
edge(y,x);
}
pre(,);
dfs(,,);
for(int i=;i<=n;i++)printf("%d\n",ans[i]);
return ;
}
CF1009F Dominant Indices
P.S. 调试的时候改小了数组,提交的时候忘改回去了......改回去之后直接A掉了......有点桑心哈哈哈
[CF1009F] Dominant Indices (+dsu on tree详解)的更多相关文章
- dsu on tree详解
这个算法还是挺人性化的,没有什么难度 就是可能看起来有点晕什么的. 大体 思想是 利用重链刨分来优化子树内部的查询. 考虑一个问题要对每个子树都要询问一次.我们暴力显然是\(n^2\)的. 考虑一下优 ...
- CF1009F Dominant Indices 解题报告
CF1009F Dominant Indices 题意简述 给出一颗以\(1\)为跟的有根树,定义\(d_{i,j}\)为以\(i\)为根节点的子树中到\(i\)的距离恰好为\(j\)的点的个数,对每 ...
- CF1009F Dominant Indices——长链剖分优化DP
原题链接 \(EDU\)出一道长链剖分优化\(dp\)裸题? 简化版题意 问你每个点的子树中与它距离为多少的点的数量最多,如果有多解,最小化距离 思路 方法1. 用\(dsu\ on\ tree\)做 ...
- CF1009F Dominant Indices(树上DSU/长链剖分)
题目大意: 就是给你一棵以1为根的树,询问每一个节点的子树内节点数最多的深度(相对于这个子树根而言)若有多解,输出最小的. 解题思路: 这道题用树链剖分,两种思路: 1.树上DSU 首先想一下最暴力的 ...
- 【算法】关于图论中的最小生成树(Minimum Spanning Tree)详解
本节纲要 什么是图(network) 什么是最小生成树 (minimum spanning tree) 最小生成树的算法 什么是图(network)? 这里的图当然不是我们日常说的图片或者地图.通常情 ...
- CF1009F Dominant Indices(启发式合并)
You are given a rooted undirected tree consisting of nn vertices. Vertex 11 is the root. Let's denot ...
- 二叉查找树(binary search tree)详解
二叉查找树(Binary Search Tree),也称二叉排序树(binary sorted tree),是指一棵空树或者具有下列性质的二叉树: 若任意节点的左子树不空,则左子树上所有结点的值均小于 ...
- BTree和B+Tree详解
https://www.cnblogs.com/vianzhang/p/7922426.html B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引.B+树中的B代表平 ...
- ODT(old driver tree)详解(带例题)
文章目录 ODT简介 实现前提&&实现原理 初始化 split操作 assign操作 其它操作 区间第k小 区间加 区间所有数的k次方和 几道水题 ODT简介 ODT(old driv ...
随机推荐
- UVA 10806 最小费用最大流
终于可以写这道题的题解了,昨天下午纠结我一下下午,晚上才照着人家的题解敲出来,今天上午又干坐着想了两个小时,才弄明白这个问题. 题意很简单,给出一个无向图,要求从1 到 n最短路两次,但是两次不允许经 ...
- Java 开发者必须了解的 16 个Java 顶级开源项目!
本文已经收录自笔者开源的 JavaGuide: https://github.com/Snailclimb/JavaGuide ([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核 ...
- 递归与树的写法-多种支付的设计-支付的接通-celery订单的回退实现
递归与树的写法 data: data=[ {"cat_id":1,"name":"北京","parent_id":0}, ...
- [Shoi2013]超级跳马(DP+矩阵乘法)
设f[i][j]表示方案数,显然有一个O(m2n)的暴力DP法,但实际上可以按距离当前位置的奇偶性分成s1[i][j]和s2[i][j],然后这个暴力DP可以优化到O(nm)的暴力.于是有这样的递推式 ...
- 新年在家学java之基础篇-高级类的特性
继承 extends 子类的共性代码都是继承自父类的,每个子类只要写自己特有的代码 class 子类 extends 父类 继承提高了代码的复用性,提供了多态的前提,但是不要为了某个功能去继承 子类不 ...
- Python笔记_第一篇_面向过程_第一部分_5.Python数据类型之元组类型(tuple)
元组!在Python中元组是属于列表的一种延伸,也是一种有序集合,成为一种只读列表,即数据可以被查找,不能被修改,列表的切片操作同样适用于元组. 特点:1. 与列表非常相似. 2. 一旦初始化就不能修 ...
- MySQL 基础知识梳理
MySQL 的安装方式有多种,但是对于不同场景,会有最适合该场景的 MySQL 安装方式,下面就介绍一下 MySQL 常见的安装方法,包括 rpm 安装,yum 安装,通用二进制安装以及源码编译安装, ...
- BaseAdapter教程(2) BaseAdapter的notifyDataSetChanged动态刷新
遇到了这麽一个需求,ListView滑到最底,然后会自动在底部加入新的Cell,实现动态刷新. 1. 首先,为ListView加上setOnScrollListener. lvHomePostItem ...
- replace|同时替换
a= 'eeekkksksksk' print a.replace('e','s').replace('s','k') #kkkkkkkkkkkk change={"e":&quo ...
- Lua-文件操作
简单模式和完全模式 简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作. 完全模式(complete model) 使用外部的文件句柄来实现.它 ...