题目大意:

就是给你一棵以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. php实现模拟登陆

    在不考虑验证码的情况一下,php实现模拟登陆,网上给的办法通常是採用curl来模拟实现,可是curl实现的是server端与server端建立了会话,仅仅能模拟登陆之后获取登陆之后的数据.无法将coo ...

  2. 【POJ3377】Ferry Lanes 最短路

    我仅仅是贴一下手写堆优化的dij模板.尽管.它.TLE了--**** #include <cstdio> #include <cstring> #include <ios ...

  3. nyoj--19--擅长排列的小明(dfs)

    擅长排列的小明 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 小明十分聪明,而且十分擅长排列计算.比如给小明一个数字5,他能立刻给出1-5按字典序的全排列,如果你想为难 ...

  4. 解决Maven项目相互依赖/循环依赖/双向依赖的问题

    转自:https://blog.csdn.net/leolu007/article/details/53079875 添加新随笔很​多​时​候​随​着​项​目​的​膨​胀​,模​块​会​越​来​越​多 ...

  5. 2015上海网络赛 HDU 5475 An easy problem 线段树

    题意就不说了 思路:线段树,维护区间乘积.2操作就将要除的点更新为1. #include<iostream> #include<cstdio> #include<cstr ...

  6. 设计兼容不同的屏幕尺寸的Android界面

    Android的屏幕类型有几百种不同的尺寸,从小型的手机到大型的电视机.因此要使我们的应用程序兼容不同屏幕尺寸,才可以让我们的应用提供给更多的用户使用. 一.支持不同的屏幕尺寸 1.使用"w ...

  7. XML结构,写到TreeView树上

    http://blog.csdn.net/ztzi321/article/details/44077563

  8. vue-router学习例子分享

    http://blog.csdn.net/bboyjoe/article/details/52804988 <!DOCTYPE html> <html lang="en&q ...

  9. angularjs 页面缓存及动态刷新解决方案

    一.准备工作 框架:angularjs ui组件库:ionic1 二.页面缓存cache 路由设置cache参数,true为缓存,false为不缓存,代码如下: angular.module('app ...

  10. cache基本结构

    下图为direct mapped     set associative        fully associative图示 direct mapped,相当于set number为1 fully ...