传送门
代码:
长链剖分好题。
题意:给你一棵树,问树上选三个互不相同的节点,使得这个三个点两两之间距离相等的方案数。
思路:
先考虑 d p dp dp。
f i , j f_{i,j} fi,j表示 i i i子树中离 i i i距离为 j j j的点数, g i , j g_{i,j} gi,j表示 i i i子树中所有满足 d i s t ( l c a ( u , v ) , i ) − d i s t ( l c a ( u , v ) , i ) = j dist(lca(u,v),i)-dist(lca(u,v),i)=j dist(lca(u,v),i)−dist(lca(u,v),i)=j的点对数。
不难发现这两个状态是可以进行拼接的。
因此对于一对父亲和孩子 ( p , v ) (p,v) (p,v)有如下转移式:
a n s + = g v , i + 1 ∗ f p , i + g p , i ∗ f v , i − 1 ans+=g_{v,i+1}*f_{p,i}+g_{p,i}*f_{v,i-1} ans+=gv,i+1∗fp,i+gp,i∗fv,i−1
g p , i + = g v , i − 1 + f v , i − 1 ∗ f p , i g_{p,i}+=g_{v,i-1}+f_{v,i-1}*f_{p,i} gp,i+=gv,i−1+fv,i−1∗fp,i
f p , i + = f v , i − 1 f_{p,i}+=f_{v,i-1} fp,i+=fv,i−1
然后用长链剖分优化一下即可。
代码:
#include
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
typedef long long ll;
const int N=1e5+5;
vector<int>e[N];
int n,hson[N],dep[N],mdep[N],len[N],fa[N];
ll tmp[N<<2],*f[N],*g[N],*now=tmp,ans=0;
void dfs1(int p){
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p])continue;
fa[v]=p,dep[v]=mdep[v]=dep[p]+1,dfs1(v),mdep[p]=max(mdep[p],mdep[v]);
if(mdep[v]>mdep[hson[p]])hson[p]=v;
}
len[p]=mdep[p]-dep[p]+1;
}
void dfs2(int p){
if(hson[p])f[hson[p]]=f[p]+1,g[hson[p]]=g[p]-1,dfs2(hson[p]);
f[p][0]=1,ans+=g[p][0];
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p]||v==hson[p])continue;
f[v]=now,now+=2*len[v],g[v]=now,now+=2*len[v],dfs2(v);
for(ri j=0;j<len[v];++j){
ans+=f[v][j]*g[p][j+1];
if(j)ans+=g[v][j]*f[p][j-1];
}
for(ri j=0;j<len[v];++j){
g[p][j+1]+=f[v][j]*f[p][j+1];
if(j)g[p][j-1]+=g[v][j];
f[p][j+1]+=f[v][j];
}
}
}
int main(){
freopen("lx.in","r",stdin);
while(n=read(),n){
ans=0;
memset(tmp,0,sizeof(tmp));
memset(hson,0,sizeof(hson));
memset(mdep,0,sizeof(mdep));
for(ri i=1;i<=n;++i)e[i].clear();
for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
dfs1(1),now=tmp,f[1]=now,now+=2*len[1],g[1]=now,now+=2*len[1],dfs2(1),cout<<ans<<'\n';
}
return 0;
}