HDU6446
题意:给一棵树,n个点,n-1条边,求它n个点全排列(一共n!种)下的所有答案和,对于每种全排列,比如n=5,点的全排列为31245,则求3-1,1-2,2-4,4-5的所有距离的和。最后答案模1e9+7。
思路:对于所有的全排列来看,我们可以先考虑两个点u,v产生的贡献,u和v在全排列中一共有n-1的位置,其他n-2个点有(n-2)!中可能,两个点的位置还可以交换,所以对于任意一对相邻的点来说,它产生的贡献为2*(n-1)*(n-2)!*s(距离)。
然后问题就是求任意两个点之间的距离了,这可以转化为求每条边的贡献,对于每条边来说,它所产生的贡献就是通过这条边,把树分成左右两部分,两部分的点数相乘,就是这条边要贡献的次数了,通过dfs很容易求出每条边左右两边的点数,答案就出来了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
#define ll long long
const int mod=1e9+7;
int head[maxn],nxt[maxn],edge[maxn],ver[maxn];
int tot=0;
int n;
void add(int u,int v,int w)
{
ver[++tot]=v;
edge[tot]=w;
nxt[tot]=head[u];
head[u]=tot;
}
int d[maxn],cnt;
int dfs(int u,int fa,int w1)
{
int sum=1;
for(int i=head[u]; i; i=nxt[i])
{
int v=ver[i];
int w=edge[i];
if(v==fa) continue;
sum+=dfs(v,u,w);
}
d[++cnt]=(ll)sum*(n-sum)*w1%mod;
return sum;
}
int main()
{
while(~scanf("%d",&n))
{
tot=0;
memset(head,0,sizeof(head));
memset(nxt,0,sizeof(nxt));
memset(d,0,sizeof(d));
for(int i=1; i<=n-1; i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
cnt=0;
dfs(1,0,0);
ll sum=0;
for(int i=1; i<=cnt; i++)
sum+=d[i]%mod;
for(int i=1; i<=n-1; i++)
sum=sum*i%mod;
sum=sum*2%mod;
printf("%d\n",sum);
}
}