Hotel[POI2014]

题意

一棵树,求所有三个不同的点,两两距离相同的方案数。

$n\leq 5000$

加强版:$n\leq 10^5$

题解

两个点可以在 $\mbox {LCA }$处统计,尝试着三个点也在 $\mbox LCA$ 那里统计答案?

三个点两两距离相同,说明必定存在一个点,使得这个点到这三个点距离均为 $d$。

1

那么假设 $3$ 号点与 $\mbox {LCA}$ 的距离为 $j$,那么蓝点到 $\mbox{LCA}$ 的距离为 $d-j$。

因此可以设 $f_{i,j}$ 表示 $i$ 号点的子树中与 $i$ 距离为 $j$ 的点有多少个,设 $g_{i,j}$ 表示 $i$ 号点的子树中,某两个点到第三点的距离为 $d$,第三点到 $\mbox{LCA}$ 的距离为 $d-j$ 的方案数。

依次考虑每个儿子 $k$,DP方程大概长这样:

$$g_{i,j}=g’{i,j}+g{k,j+1}+f_{i,j}\times f_{k,j-1},\f_{i,j+1}=f’{i,j+1}+f{k,j}$$

统计答案:$g_{i,0}+\sum{f_{i,j}g_{k,j-1}}+\sum{f_{i,j-1}g_{k,j}}$

每一个 $k$ 的时间是 $O(len_k)$ 的。

显然可以 $O(n^2)$ DP一下。

既然复杂度跟 $len_k$ 相关,那么长链剖分一下就可以了。

重儿子的转移:$f_{son_i}=f_i+1,g_{son_i}=g_i-1$

预留够足够的空间就可以了。

时间复杂度 $O(n)$。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#define N 100010
int ver[N<<1],ne[N<<1],head[N],tot;
inline void add(int x,int y)
{
ver[++tot]=y; ne[tot]=head[x]; head[x]=tot;
ver[++tot]=x; ne[tot]=head[y]; head[y]=tot;
}
int n,len[N],son[N];
ll tmp[N<<2],*f[N],*g[N],*id=tmp,ans;
void dfs1(int u,int pre)
{
for(int i=head[u],v;i;i=ne[i])
if((v=ver[i])!=pre)
{
dfs1(v,u);
if(len[son[u]]<len[v]) son[u]=v;
}
len[u]=len[son[u]]+1;
}
void dfs2(int u,int pre)
{
if(son[u]) g[son[u]]=g[u]-1,f[son[u]]=f[u]+1,dfs2(son[u],u);
ans+=g[u][0]; f[u][0]=1;
for(int i=head[u],v;i;i=ne[i])
if((v=ver[i])!=pre&&ver[i]!=son[u])
{
f[v]=id; id+=len[v]<<1;
g[v]=id; id+=len[v]<<1;
dfs2(v,u);
fo(j,0,len[v]-1)
{
ans+=g[u][j+1]*f[v][j];
if(j!=len[v]-1) ans+=g[v][j+1]*f[u][j];
}
fo(j,0,len[v]-1)
{
g[u][j+1]+=f[u][j+1]*f[v][j];
if(j) g[u][j-1]+=g[v][j];
f[u][j+1]+=f[v][j];
}
}
}
int main()
{
n=read();
fo(i,2,n) add(read(),read());
dfs1(1,0);
f[1]=id; id+=len[1]<<1;
g[1]=id; id+=len[1]<<1;
dfs2(1,0); printf("%lld",ans);
return 0;
}