水了半个月之后Fd终于开始做题啦!
然后成功的发现自己什么都不会了
树形DP,既然是三个点两两距离相等那一定得有个中心点吧,枚举那个中心点,然后暴力DFS,根据乘法原理算。
乘法原理就是我一个子树,距离为i的选择情况增加tot[i],两个子树的话是一个子树的选择情况乘上tot[i],三个子树(就是答案)就是两个子树的选择情况乘上tot[i];
挺暴力的……不过貌似POI能过了
据说有个加强版100000数据范围,要什么长链剖分……记一下以后来填坑
#include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #include<algorithm> #define maxn 5010 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch==‘-‘) f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-‘0‘; ch=getchar(); } return num*f; } struct Edge{ int next,to; }edge[maxn*2]; int head[maxn],num; inline void add(int from,int to){ edge[++num]=(Edge){head[from],to}; head[from]=num; } int dis[maxn]; int one[maxn]; int two[maxn]; int tot[maxn]; void dfs(int x,int fa){ tot[dis[x]]++; for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to==fa) continue; dis[to]=dis[x]+1; dfs(to,x); } return; } long long ans; int main(){ int n=read(); for(int i=1;i<n;++i){ int x=read(),y=read(); add(x,y); add(y,x); } for(int i=1;i<=n;++i){ memset(one,0,sizeof(one)); memset(two,0,sizeof(two)); dis[i]=0; for(int j=head[i];j;j=edge[j].next){ int to=edge[j].to; memset(tot,0,sizeof(tot)); dis[to]=1; dfs(to,i); for(int k=1;k<=n;++k){ ans+=1LL*two[k]*tot[k]; two[k]+=tot[k]*one[k]; one[k]+=tot[k]; } } } printf("%lld\n",ans); return 0; }
原文地址:https://www.cnblogs.com/cellular-automaton/p/8343587.html
时间: 2024-10-30 21:13:38