题目大意:

就是给你一棵以1为根的树,询问每一个节点的子树内节点数最多的深度(相对于这个子树根而言)若有多解,输出最小的。

解题思路:

这道题用树链剖分,两种思路:

1.树上DSU

首先想一下最暴力的算法:统计子树每个深度节点的个数(桶)相当于以每个节点为根遍历子树搜索一遍答案,这样做时间复杂度是O(n2),显然过不去。

考虑一下优化。假如说我们模拟一下搜索答案的过程,我们发现在每一次暴搜时都会在桶中添加一些答案。而这些答案的整体只会对该节点及其祖先产生贡献,也就是说,只有该节点以及其祖先的桶中才一定会有这个答案的整体,也只会对该节点及以下非同祖子树答案产生干扰。也就是说,搜索一棵树时,其答案可以直接加到其祖先上,所以对于最后一颗子树,因为之后不会再干扰其他子树所以其答案可以直接上传至父节点。那么这个最后搜索的节点的子树应该越大越好,那么就可以使用树链剖分解决了。答案更新时,最后搜最大的即可。

对于时间复杂度,每次搜索最坏为O(n),最大儿子上传是O(1),小儿子上传为O(n),轻儿子log2n个所以时间复杂度为O(logn)

上代码:

 #include<cstdio>
#include<cstring>
#include<algorithm>
struct pnt{
int hd;
int dp;
int wgt;
int mxs;
int ans;
}p[];
struct ent{
int twd;
int lst;
}e[];
struct pr{
int x,y;
bool friend operator == (pr a,pr b){return (a.x==b.x)&&(a.y==b.y);}
bool friend operator > (pr a,pr b){if(a.y==b.y)return a.x<b.x;return a.y>b.y;}
bool friend operator < (pr a,pr b){if(a.y==b.y)return a.x>b.x;return a.y<b.y;}
bool friend operator <= (pr a,pr b){return ((a<b)||(a==b));}
bool friend operator >= (pr a,pr b){return ((a>b)||(a==b));}
};
class Prq{
public:
bool bol(void)
{
return (bool)(siz==);
}
void maxs(int &b)
{
b=line[].x;
}
void del(void)
{
line[]=line[siz--];
int nw,nx;
nw=;
while((nw<<)<=siz)
{
nx=nw<<;
if(nx<siz&&line[nx]<line[nx+])
nx++;
if(line[nx]<=line[nw])
break;
std::swap(line[nw],line[nx]);
nw=nx;
}
return ;
}
void ins(int a,int b)
{
pr tmp=(pr){a,b};
line[++siz]=tmp;
int nx,nw;
nw=siz;
while(nw>)
{
nx=nw>>;
if(line[nx]>=line[nw])
break;
std::swap(line[nx],line[nw]);
nw=nx;
}
return ;
}
void dst(void)
{
siz=;
return ;
}
private:
pr line[];
int siz;
}Q;
int n;
int cnt;
int num[];
void ade(int f,int t)
{
cnt++;
e[cnt].twd=t;
e[cnt].lst=p[f].hd;
p[f].hd=cnt;
}
void Basic_dfs(int x,int f)
{
p[x].dp=p[f].dp+;
p[x].wgt=;
int maxs=-;
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f)
continue;
Basic_dfs(to,x);
p[x].wgt+=p[to].wgt;
if(maxs<p[to].wgt)
{
maxs=p[to].wgt;
p[x].mxs=to;
}
}
}
void Build_dfs(int x,int f)
{
num[p[x].dp]++;
Q.ins(p[x].dp,num[p[x].dp]);
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f)
continue;
Build_dfs(to,x);
}
}
void Destory_dfs(int x,int f)
{
num[p[x].dp]--;
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f)
continue;
Destory_dfs(to,x);
}
}
void DSU_dfs(int x,int f,bool hvs)
{
if(!x)
return ;
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f||to==p[x].mxs)
continue;
DSU_dfs(to,x,false);
}
DSU_dfs(p[x].mxs,x,true);
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f||to==p[x].mxs)
continue;
Build_dfs(to,x);
}
num[p[x].dp]++;
Q.ins(p[x].dp,num[p[x].dp]);
Q.maxs(p[x].ans);
p[x].ans-=p[x].dp;
if(hvs)
return ;
Destory_dfs(x,f);
Q.dst();
return ;
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ade(x,y);
ade(y,x);
}
Basic_dfs(,);
DSU_dfs(,,true);
for(int i=;i<=n;i++)
printf("%d\n",p[i].ans);
return ;
}

2.树的长链剖分:

这个方法比上面的方法跑得快。

首先,观察那种最朴素的全搜一遍的O(n2)算法,它的瓶颈在于,统计答案时同一深度非同父的节点,其答案可能互相干扰,那么我们为何不合理安排内存使其答案不会互相被访问到而会同时被祖先访问到。类似于一个树链剖分序。类似长链先搜,短链后搜的剖分序。使用不同的下标索引使桶中的变量不会在深度环境下发生冲突,再logn统计答案就可以了,其实是O(nlogn)但重建部分比较简单常数较小且其最坏复杂度很难达到所以速度相当惊人。

上代码:

 #include<cstdio>
#include<cstring>
#include<algorithm>
struct pnt{
int hd;
int dp;
int mxs;
bool vis;
int ind;
int ans;
int wsa;
}p[];
struct ent{
int twd;
int lst;
}e[];
int n,m;
int cnt;
int wh;
int tmp[];
void ade(int f,int t)
{
cnt++;
e[cnt].twd=t;
e[cnt].lst=p[f].hd;
p[f].hd=cnt;
}
void Basic_dfs(int x,int f)
{
p[x].dp=p[f].dp+;
p[x].wsa=p[x].dp;
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f)
continue;
Basic_dfs(to,x);
p[x].wsa=std::max(p[x].wsa,p[to].wsa);
if(p[to].wsa>p[p[x].mxs].wsa)
{
p[x].mxs=to;
}
}
}
void Gund_dfs(int x,int f)
{
tmp[p[x].ind]=;
if(p[x].mxs)
{
p[p[x].mxs].ind=p[x].ind+;
Gund_dfs(p[x].mxs,x);
p[x].ans=p[p[x].mxs].ans+;
}
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f||to==p[x].mxs)
continue;
p[to].ind=wh;
wh+=p[to].wsa-p[to].dp+;
Gund_dfs(to,x);
for(int j=;j<=p[to].wsa-p[to].dp;j++)
{
tmp[p[x].ind+j+]+=tmp[p[to].ind+j];
if(tmp[p[x].ind+j+]>tmp[p[x].ind+p[x].ans]||(tmp[p[x].ind+j+]==tmp[p[x].ind+p[x].ans]&&p[x].ans>j+))
p[x].ans=j+;
}
}
if(tmp[p[x].ind+p[x].ans]==)
p[x].ans=;
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ade(x,y);
ade(y,x);
}
Basic_dfs(,);
wh=p[].wsa;
Gund_dfs(,);
for(int i=;i<=n;i++)
printf("%d\n",p[i].ans);
return ;
}

CF1009F Dominant Indices(树上DSU/长链剖分)的更多相关文章

  1. 【CF1009F】Dominant Indices(长链剖分)

    [CF1009F]Dominant Indices(长链剖分) 题面 洛谷 CF 翻译: 给定一棵\(n\)个点,以\(1\)号点为根的有根树. 对于每个点,回答在它子树中, 假设距离它为\(d\)的 ...

  2. Codeforces 1009 F. Dominant Indices(长链剖分/树上启发式合并)

    F. Dominant Indices 题意: 给一颗无向树,根为1.对于每个节点,求其子树中,哪个距离下的节点数量最多.数量相同时,取较小的那个距离. 题目: 这类题一般的做法是树上的启发式合并,复 ...

  3. CF1009F Dominant Indices——长链剖分优化DP

    原题链接 \(EDU\)出一道长链剖分优化\(dp\)裸题? 简化版题意 问你每个点的子树中与它距离为多少的点的数量最多,如果有多解,最小化距离 思路 方法1. 用\(dsu\ on\ tree\)做 ...

  4. 【CF1009F】 Dominant Indices (长链剖分+DP)

    题目链接 \(O(n^2)\)的\(DP\)很容易想,\(f[u][i]\)表示在\(u\)的子树中距离\(u\)为\(i\)的点的个数,则\(f[u][i]=\sum f[v][i-1]\) 长链剖 ...

  5. 【CF1009F】Dominant Indices(长链剖分优化DP)

    点此看题面 大致题意: 设\(d(x,y)\)表示\(x\)子树内到\(x\)距离为\(y\)的点的个数,对于每个\(x\),求满足\(d(x,y)\)最大的最小的\(y\). 暴力\(DP\) 首先 ...

  6. CF1009F Dominant Indices 长链剖分

    题目传送门 https://codeforces.com/contest/1009/problem/F 题解 长链剖分的板子吧. 令 \(dp[x][i]\) 表示 \(x\) 的子树中的深度为 \( ...

  7. 【Cf Edu #47 F】Dominant Indices(长链剖分)

    要求每个点子树中节点最多的层数,一个通常的思路是树上启发式合并,对于每一个点,保留它的重儿子的贡献,暴力扫轻儿子将他们的贡献合并到重儿子里来. 参考重链剖分,由于一个点向上最多只有$log$条轻边,故 ...

  8. CF 1009 F Dominant Indices —— 长链剖分+指针

    题目:http://codeforces.com/contest/1009/problem/F 也可以用 dsu on tree 的做法,全局记录一个 dep,然后放进堆里,因为字典序要最小,所以再记 ...

  9. 2019.01.08 codeforces 1009F. Dominant Indices(长链剖分)

    传送门 长链剖分模板题. 题意:给出一棵树,设fi,jf_{i,j}fi,j​表示iii的子树中距离点iii距离为jjj的点的个数,现在对于每个点iii要求出使得fif_ifi​取得最大值的那个jjj ...

随机推荐

  1. HDFS中的命令行

    HDFS中的命令行 本文介绍了HDFS以命令行执行的时候.几个经常使用的命令行的作用和怎样使用~ 1. fs fs是启动命令行动作,该命令用于提供一系列子命令. 使用形式为hadoop fs –cmd ...

  2. lightoj--1008--Fibsieve`s Fantabulous Birthday(水题)

    Fibsieve`s Fantabulous Birthday Time Limit: 500MS   Memory Limit: 32768KB   64bit IO Format: %lld &a ...

  3. Integer 和 int的种种比较

    public static void main(String[] args) { int i = 128; Integer i2 = 128; Integer i3 = new Integer(128 ...

  4. SpringMVC+MyBatis (druid、logback)

    数据库连接池是阿里巴巴的druid.日志框架式logback 1.整合SpringMVCspringMybatis-servlet.xml: <?xml version="1.0&qu ...

  5. ES6学习笔记(六)数组的扩展

    1.扩展运算符 1.1含义 扩展运算符(spread)是三个点(...).它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列. console.log(...[1, 2, 3]) // ...

  6. Windows10 Linux子系统的启用和中文用户名的修改

    一直用的虚拟机Linux,忽然心血来潮,看到Windows 10可以使用Linux子系统,于是来装一波,按照这位前辈的教程 https://blog.csdn.net/zhangdongren/art ...

  7. 彻底分析虚拟视频驱动vivi(三)

    在Ubuntu系统中接上usb摄像头设备时,系统会自动安装对应的usb设备驱动程序.我们现在要使用自己编译的vivi驱动,该怎么办呢? 1.先安装系统自带的vivi驱动和它所依赖的所有驱动:sudo ...

  8. log4j小结

    核心包: org.apache.log4j 三大组件 Loggers 日志操作 Appenders 日志的展现形式 Layouts 日志的展现格式 日志等级 TRACE DEBUG INFO WARN ...

  9. SQL Server 函数的使用 Function

    create table student ( id varchar2(5) primary key, name varchar2(20) not null, sex char(2) check(sex ...

  10. [C/C++标准库]_[0基础]_[使用fstream合并文本文件]

    场景: 1. 就是合并文本文件,而且从第2个文件起不要合并第一行. 2. 多加了一个功能,就是支持2个以上的文件合并. 3. 问题: http://ask.csdn.net/questions/192 ...