题意:
给出一棵树,每条边长度为1。问找三个点,使他们两两距离相等有多少方案。
n<=100000(poi原题为5000)
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define N 110000
#define LL long long
using namespace std;
struct node{int y,nex;}a[2*N];
LL id,*f[N],*g[N],mem[10*N],ans;
int fir[N],len,son[N],dep[N],h[N],mx[N],n,siz[N];
void ins(int x,int y)
{
a[++len].y=y;a[len].nex=fir[x];fir[x]=len;
}
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1;mx[x]=x;
for(int k=fir[x];k;k=a[k].nex)
{
int y=a[k].y;
if(y==fa) continue;
dfs(y,x);
if(dep[mx[y]]>dep[mx[son[x]]]) son[x]=y;
if(dep[mx[y]]>dep[mx[x]]) mx[x]=mx[y];
}
siz[mx[x]]=h[x]=dep[mx[x]]-dep[x]+1;
}
void creat_mem()
{
for(int i=1;i<=n;i++)
{
if(mx[i]!=i) continue;
id+=siz[i]-1;
f[i]=&mem[id];
id++;
g[i]=&mem[id];
id+=2*siz[i];
id+=2;
}
}
void dp(int x,int fa)
{
if(mx[x]==x) {f[x][0]=1;return;}
dp(son[x],x);
f[x]=f[son[x]]-1;
g[x]=g[son[x]]+1;
ans+=g[x][0];
f[x][0]=1;
for(int k=fir[x];k;k=a[k].nex)
{
int y=a[k].y;
if(y==fa || y==son[x]) continue;
dp(y,x);
for(int i=0;i<=h[y];i++) ans+=f[y][i]*g[x][i+1];
for(int i=1;i<=h[y];i++) ans+=g[y][i]*f[x][i-1];
for(int i=0;i<=h[y];i++) g[x][i+1]+=f[y][i]*f[x][i+1];
for(int i=1;i<=h[y];i++) g[x][i-1]+=g[y][i];
for(int i=0;i<=h[y];i++) f[x][i+1]+=f[y][i];
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(1,0);
creat_mem();
dp(1,0);
printf("%lld\n",ans);
return 0;
}
题解:
膜了题解。。太神了转述一下
显然对于每个方案(x,y,z)有且仅有一个点u使得:
1、x,y,z在u的不同子树中
2、x,y,z到u距离相等
考虑这种dp方法(不然没法做)
f[i][j]表示i子树中与i距离为j的点有多少个
g[i][j]表示i子树已经组合好了多少需要i子树外距离为j的一个点的点对
就是这个意思
(x,y)就能对g[i][j]贡献1
n2
转移是容易的
尝试对每个点找一个重孩子,即若x子树中最深叶节点在孩子y子树中,y就是x的重孩子。
定义每个点的高度h[x]为到子树中最深叶节点的距离,将沿重孩子走的链叫重链。
考虑那个
n2
dp的第一次转移
对于x的第一个孩子y,我们是不是令f[x][i]=f[y][i-1],g[x][i]=g[y][i+1]?
发现是平移,利用数组的地址是连续的,将f[x][1]的地址指向f[y][0],将g[x][0]的地址指向g[y][1]就能
O(1)
完成这一部分转移!
除了重孩子外的转移用
O(h[y])
转移即可
分析时间复杂度。点x转移的复杂度是
O(1+∑y!=sonh[y])=O(∑h[y]−h[son])=O(∑h[y]−h[x]+1)
将所有的相加,每个x在父亲处为正,在x处为负就全部抵消了!剩下的1作和就是
O(n)
由于同一条重链用的是同一个数组,事先给他分配重链长度的连续地址即可。空间复杂度也是
O(n)
的。用vector实现也是可以的,一条重链用一个vector,对于每个vector记录一下0的位置就好了><
点分治怎么做?