bzoj3522[Poi2014]Hotel*

bzoj3522[Poi2014]Hotel

题意:

在一个树上求三个点两两距离相等的方案数。n≤5000。

题解:

枚举每个点作为中心点,求出每个子树的深度为i的节点大小,则目标是求某个深度的答案和。

设第i个子树在某个深度的节点数为dep[i],令a1[i]=sigma(dep[j],j<=i);则取两个节点的答案a2[i]=dep[i]*a1[i-1],取三个节点的答案为a3[i]=dep[i]*a2[i-1]为所求,故递推求即可。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <queue>
 5 #define inc(i,j,k) for(int i=j;i<=k;i++)
 6 #define maxn 5010
 7 #define ll long long
 8 using namespace std;
 9 
10 inline int read(){
11     char ch=getchar(); int f=1,x=0;
12     while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
13     while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
14     return f*x;
15 }
16 struct e{int t,n;}es[maxn*2]; int g[maxn],ess;
17 void pe(int f,int t){
18     es[++ess]=(e){t,g[f]}; g[f]=ess; es[++ess]=(e){f,g[t]}; g[t]=ess;
19 }
20 int n,dep[maxn]; ll a1[maxn],a2[maxn],a3[maxn],ans;
21 void dfs(int x,int fa,int d){
22     dep[d]++; for(int i=g[x];i;i=es[i].n)if(es[i].t!=fa)dfs(es[i].t,x,d+1);
23 }
24 int main(){
25     n=read(); inc(i,1,n-1){int x=read(),y=read(); pe(x,y);}
26     inc(i,1,n){
27         int tot=0; memset(a1,0,sizeof(a1)); memset(a2,0,sizeof(a2)); memset(a3,0,sizeof(a3));
28         for(int j=g[i];j;j=es[j].n){
29             memset(dep,0,sizeof(dep)); dfs(es[j].t,i,1); tot++;
30             inc(k,1,n){
31                 if(tot>=3){a3[k]+=a2[k]*dep[k]; a2[k]+=a1[k]*dep[k]; a1[k]+=dep[k];}
32                 if(tot==2){a2[k]+=a1[k]*dep[k]; a1[k]+=dep[k];}
33                 if(tot==1)a1[k]+=dep[k];
34             }
35         }
36         inc(j,1,n)ans+=a3[j];
37     }
38     printf("%lld",ans); return 0;
39 }

 

20161104

转载于:https://www.cnblogs.com/YuanZiming/p/6038781.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值