陈太阳与粉丝
陈太阳与粉丝

思路

注意到这题无论选取什么样的访问顺序,最终收益都是相同的。那么随便选一种顺序算答案就好了。时间复杂度O(n)。
证明:
使用归纳法。
n=1时,显然答案与顺序无关。现在假设size<n的树的答案与顺序都无关,我们来证明size=n的树的答案与顺序无关。
若根的儿子只有一个,那么得证。现假设根的儿子至少有两个。
我们用ANS(x)表示只有根和根的儿子x以及x的子树的子问题的答案,即从根出发,巡游完x的子树并回到根的答案。
我们用SZ(x)表示以x为根的子树的大小的两倍(即从根开始巡游x这个子树再回到根的天数)。
我们假定我们访问根的儿子的顺序是s1,s2…sm,那么总的答案就为ANS(s1)+SZ(s1)(SZ(s2)+SZ(s3)…+SZ(sm))+ANS(s2)+SZ(s2)(SZ(s3)+SZ(s4)+…SZ(sm))+…+ANS(sm)。
注意到总的答案为所有儿子的ANS的和再加上两两儿子之间SZ的积的和。由于儿子至少有两个,所以儿子的子树大小一定小于n,通过归纳法得知它们的ANS与顺序无关。两两儿子之间SZ的积的和也与顺序无关。所以size=n的树的总答案与顺序无关。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,tot,sum,hed[maxn],ver[maxn<<1],nex[maxn<<1],dfn;
long long ans;
inline char gc()
{
 static char buf[1000001],*p1=buf,*p2=buf;
 return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
void read(int &x)
{
 x=0;
 char ch=gc();
 while(ch<'0'||ch>'9') 
  ch=gc();
 while(ch>='0'&&ch<='9') 
  x=x*10+(ch^48),ch=gc();
}
void add(int x,int y)
{
 ver[++tot]=y; 
 nex[tot]=hed[x]; 
 hed[x]=tot;
}
void dfs(int x,int fa)
{
 int i,y;
 ans+=sum-dfn;
 for(i=hed[x];i;i=nex[i])
 {
  y=ver[i];
  if(y==fa) 
   continue;
  ++dfn;
  dfs(y,x);
  ++dfn;
 }
}
int main(){
 int i,x,y;
 read(n);
 for(i=1;i<n;++i)
 {
  read(x);
  read(y);
  add(x,y);
  add(y,x);
 }
 sum=2*n-2;
 
 dfs(1,0);
 
 cout<<ans<<endl;
 return 0;
}

来源:zr

相关文章: