Dominant Indices

长链剖分初学笔记

长链剖分是重链剖分的兄弟,但与CZ宇宙里的兄弟不同的是,他不止三厘米,相反,他是越长越好,越长越好。长链剖分的原理是在统计信息时父亲直接继承某个孩子信息,其它孩子则暴力统计。这个孩子就是长孩子,连接它和父亲之间的边就叫长链。长孩子的定义是从这个孩子向下可以到达最深的角落,从这个定义出发可以想到这个东西的使用场景,即要求快速统计有关深度的一些信息。

长链剖分核心是长链,而使用长链剖分可以把算法的复杂度降低到\(O(NlogN)\)甚至到恐怖的\(O(N)\)。原理上面也说过了,那就是父亲继承(那玩意似乎可以总结为父继子业?很是有点大逆不道)长孩子的信息。怎么继承?既然是在统计和深度有关的内容,那么可以想到一个深深的节点,到它孩子的距离比到它的距离少了一个1(或者边长),那么把孩子内所有结点的距离都加上那个值不就是父亲的答案了吗?于是也不知道哪个天才想出了用指针解决“快速加同值”的问题……

然后就可以啦。代码实现上,口诀是统计自己,统计长孩子,关照其它孩子并给长孙子留出dp空间。注意一下就是非长儿子转移到自己时要清晰哪些dp值是能用的。

最后就是这玩意似乎不支持在线,而且要在solve中记录答案,因为数组就那么大,你不记录的话别人一调用信息不久乱套了吗……

#include<cstdio>
//#define zczc
const int N=1000010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

int m;
struct edge{
	int t,next;
}e[N<<1];
int head[N],esum;
inline void add(int fr,int to){
	esum++;
	e[esum].t=to;
	e[esum].next=head[fr];
	head[fr]=esum;
	return;
}

int son[N],d[N];
void dfs(int wh,int fa){
	d[wh]=1;
	for(int i=head[wh],th;i;i=e[i].next){
		th=e[i].t;
		if(th==fa)continue;
		dfs(th,wh);
		if(d[th]+1>d[wh])d[wh]=d[th]+1,son[wh]=th;
	}
}

int *f[N],data[N],ans[N];int *p=data;
void solve(int wh,int fa){
	f[wh][0]=1;
	if(son[wh]!=0){
		f[son[wh]]=f[wh]+1;
		solve(son[wh],wh);
		ans[wh]=ans[son[wh]]+1;
	}
	for(int i=head[wh],th;i;i=e[i].next){
		th=e[i].t;
		if(th==fa||th==son[wh])continue;
		f[th]=p;p+=d[th];
		solve(th,wh);
		for(int i=0;i<d[th];i++){
			f[wh][i+1]+=f[th][i];
			if(f[wh][i+1]>f[wh][ans[wh]])ans[wh]=i+1;
			else if(f[wh][i+1]==f[wh][ans[wh]]&&i+1<ans[wh])ans[wh]=i+1;
		}
	}
	if(f[wh][ans[wh]]==1)ans[wh]=0;
	return;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	int s1,s2;
	read(m);
	for(int i=1;i<m;i++){
		read(s1);read(s2);
		add(s1,s2);add(s2,s1);
	}
	dfs(1,0);
	f[1]=p;p+=d[1];
	solve(1,0);
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	
	return 0;
}
posted @ 2022-01-24 21:16  Feyn618  阅读(42)  评论(0编辑  收藏  举报