首页 > 其他分享 >关于长链剖分的数组实现 | CF1009F Dominant Indices

关于长链剖分的数组实现 | CF1009F Dominant Indices

时间:2023-02-04 20:00:19浏览次数:46  
标签:长链 Dominant 剖分 int son dfn ans now DP

请容许我不理解一下为什么这题题解几乎全都是指针实现/kk
其实长链剖分是可以直接用数组来写的。

考虑朴素 DP。设 \(f_{u,i}\) 表示以点 \(u\) 为根的子树中与点 \(u\) 距离为 \(i\) 的点的个数。
则转移方程为:

\[f_{u,i}=\sum\limits_{v\in son_u} f_{v,i-1} \]

答案为:

\[ans_u=\max\{f_{u,i}\} \]

转移方程以深度为下标,可以使用长链剖分优化。

在重链剖分时,我们定义一个节点的重儿子为 \(siz\) 最大的儿子,而长链剖分时则是把能到达深度最深的点作为重儿子。
这样,每次我们先对于 \(u\) 的重儿子 DP,那么点 \(u\) 可以直接继承 \(son_u\) 的 DP 值,再暴力合并其它轻儿子。
对于每个点,只会在它所在长链的顶端被暴力合并一次,时间复杂度为 \(O(n)\)。

观察到 \(f_v\) 在继承过来的时候是需要向后错一位的,这导致我们需要用一些特殊方法来存储 DP 值。
这里提供一种数组实现的思路。
对于 DP 数组 \(f\),我们把 \(f_{u,i}\) 映射到下标 \(dfn_u+i\) 的位置上。由于每次我们先遍历 \(son_u\),那么得到 \(dfn_{son_u}=dfn_u+1\)。
也就是说,\(f_{v,0}\) 和 \(f_{u,1}\) 映射的位置相同,恰好向后错了一位。
由于需要保证空间复杂度 \(O(n)\),在每求完一个点的 DP 值后统计该点的答案。

这个写法只是把一个指针数组换成了 \(dfn\) 数组,对空间复杂度似乎没啥影响(?)

#include<bits/stdc++.h>
#define il inline
using namespace std;
il int read()
{
	int xr=0,F=1; char cr=getchar();
	while(cr<'0'||cr>'9') {if(cr=='-') F=-1;cr=getchar();}
	while(cr>='0'&&cr<='9') 
		xr=(xr<<3)+(xr<<1)+(cr^48),cr=getchar();
	return xr*F;
}
const int N=1e6+5;
int n;
struct edge{
	int nxt,to;
}e[N<<1];
int head[N],cnt; 
void add(int u,int v){
	e[++cnt]={head[u],v};head[u]=cnt;
}
int dep[N],son[N],dfn[N],tot;
void dfs1(int now,int fa)
{
	for(int i=head[now];i;i=e[i].nxt)
	{
		int v=e[i].to;if(v==fa) continue;
		dfs1(v,now);
		if(dep[v]>dep[son[now]]) son[now]=v;
	}
	dep[now]=dep[son[now]]+1;
}
int f[N],ans[N]; 
void dfs2(int now,int fa)
{
	dfn[now]=++tot;
	f[dfn[now]]=1;
	if(son[now])
	{
		dfs2(son[now],now);
		if(f[dfn[son[now]]+ans[son[now]]]>1) ans[now]=ans[son[now]]+1;
	}
	for(int i=head[now];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==son[now]||v==fa) continue;
		dfs2(v,now);
		for(int j=1;j<=dep[v];j++) 
		{
			f[dfn[now]+j]+=f[dfn[v]+j-1];
			if(f[dfn[now]+j]>f[dfn[now]+ans[now]]||
			f[dfn[now]+j]==f[dfn[now]+ans[now]]&&j<ans[now]) ans[now]=j;
		}
	}
}
int main()
{
	n=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	dfs1(1,0),dfs2(1,0);
	for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
	return 0;
}

不知道是不是因为我又孤陋寡闻了,但真的一篇数组实现都搜不到/kk
所以写了这篇博客试图帮助和我一样不会指针的蒟蒻 QAQ

标签:长链,Dominant,剖分,int,son,dfn,ans,now,DP
From: https://www.cnblogs.com/ying-xue/p/17092237.html

相关文章

  • 树链剖分
    dfs序与树链剖分dfs序比如dfs序为:ABDGHICEJF时间戳时间戳即dfs每次访问到每个节点的时间,时间从1开始累加比如上图时间戳为用处把树强行搞成连续的......
  • Codeforces-343D Water Tree(树链剖分)
    Description:MadscientistMikehasconstructedarootedtree,whichconsistsof n vertices.Eachvertexisareservoirwhichcanbeeitheremptyorfilledw......
  • SPOJ375--Query on a tree(树链剖分)
    Description:Youaregivenatree(anacyclicundirectedconnectedgraph)with N nodes,andedgesnumbered1,2,3...N-1.Wewillaskyoutoperfromsomeins......
  • 树链剖分
    “在一棵树上进行路径的修改、求极值、求和”乍一看只要线段树就能轻松解决,实际上,仅凭线段树是不能搞定它的。我们需要用到一种貌似高级的复杂算法——树链剖分。树链剖分......
  • 树链剖分入门
    目录树链剖分算法思想模版-树链剖分旅行P4374P4211CF1023FP1505P2486P7735P3976Trick总结树链剖分这玩意也是才开始预习,写得不好勿喷。约定记号:\(siz[x]\),\(x\)为根的......
  • 树链剖分学习笔记
    怕到时候忘了,来写一篇笔记前置芝士:树的存储与遍历,\(dfs\)序,线段树。树链剖分的思想及能解决的问题:树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息。具体......
  • 树链剖分
    简述树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息。具体来说,将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息。树链剖分(树剖/链......
  • 【YBT2023寒假Day1 B】不跪模样(树链剖分)(线段树)
    不跪模样题目链接:YBT2023寒假Day1B题目大意给你一棵有根数,点有点权,两种操作:对于所有x子树内与x距离不超过2的点,将其点权加v。询问x子树中,满足i<=j且i,j......
  • 轻重链剖分板子
    //LuoguP3384【模板】轻重链剖分/树链剖分#include<iostream>#include<cstring>#include<algorithm>#include<vector>#definelcu<<1#definercu<<1|1usin......
  • 2568. 树链剖分
    2568.树链剖分原题链接思路:先将一颗树进行树链剖分,再将它的dfs序求出来利用dfs序将线段树模拟出来(build出来)再将输入的路径进行切割导入线段树处理,直到两个元素......