【CF1009F】Dominant Indices

题目

长链剖分板子题啊

长链剖分有几个神奇的性质

  1. 所有长链的总点数为(n)

  2. 任意一个点的(k)级祖先所在长链的长度肯定不小于(k)

  3. 从任意点到根经过的短边数量不超过(sqrt{n}),也就是用长链剖分求(LCA)是根号复杂度的。。

最后一个性质是这样的,从一个点往上经过一条短边,点数要加(1),再经过一条短边,点数要加(2),经过一共(k)条短边,点数要加(frac{k(k+1)}{2}),于是短边次数就是(sqrt{n})级别的

之后长链剖分可以(O(1))(k)级祖先,不过好像没什么用啊,最主要的作用还是用来优化一些树上跟深度有关系的转移,可以做到均摊(O(n))复杂度

比如说这道题我们设(f[x][j])表示在(x)的子树中距离(x)深度为(j)的点有多少个

我们需要另(x)直接继承其长儿子的状态,就是(f[x][j+1]=f[son[x]][j]),这个需要用指针(O(1))实现

之后对于所有的短儿子,我们直接暴力合并过去

我们发现这样之后在一个点不是其父亲的长儿子的时候会暴力合并,代价是这条长链的长度,根据性质(1),所有长链的长度为(n),所以这个算法是均摊(O(n))

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=1000005;
inline int read() {
	char c=getchar();int x=0;while(c<'0'||x>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
int head[maxn],deep[maxn],len[maxn],son[maxn];
int n,num,tax[maxn],*id=tax,*f[maxn],ans[maxn];
inline void add(int x,int y) {
	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void dfs1(int x) {
	int maxx=-1;
	for(re int i=head[x];i;i=e[i].nxt) {
		if(deep[e[i].v]) continue;
		deep[e[i].v]=deep[x]+1;
		dfs1(e[i].v);
		if(len[e[i].v]>maxx) maxx=len[e[i].v],son[x]=e[i].v;
	}
	len[x]=len[son[x]]+1;
}
void dfs(int x) {
	f[x][0]=1;
	if(son[x]) f[son[x]]=f[x]+1,dfs(son[x]),ans[x]=ans[son[x]]+1;
	for(re int i=head[x];i;i=e[i].nxt) {
		if(deep[e[i].v]<deep[x]||e[i].v==son[x]) continue;
		f[e[i].v]=id;id+=len[e[i].v];dfs(e[i].v);
		for(re int j=1;j<=len[e[i].v];j++) {
			f[x][j]+=f[e[i].v][j-1];
			if(f[x][j]>f[x][ans[x]]||(f[x][j]==f[x][ans[x]]&&j<ans[x]))
				ans[x]=j;
		}
	}
	if(f[x][ans[x]]==1) ans[x]=0;
}
int main() {
	n=read();
	for(re int x,y,i=1;i<n;i++)
		x=read(),y=read(),add(x,y),add(y,x);
	deep[1]=1,dfs1(1);
	f[1]=id,id+=len[1];dfs(1);
	for(re int i=1;i<=n;i++) printf("%d
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/10675041.html