思路
注意到这题无论选取什么样的访问顺序,最终收益都是相同的。那么随便选一种顺序算答案就好了。时间复杂度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