HDU6446

 

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);
    }
}

 

posted @ 2019-06-22 20:07  paranoid。  阅读(184)  评论(0编辑  收藏  举报