HDU 6446 Tree and Permutation 树形DP

  • 题目链接 :http://acm.hdu.edu.cn/showproblem.php?pid=6446

  • 题意 :有n个节点,它们在一条边上排列,每个排列的权重和是从1-2-……-n的距离和,求n!个排列权重和的总和。

  • 思路 :对于每一个排列,求从1走到n的路径和,考虑每个点对的贡献。
    1)对于一个点对,在n!个排列中,他们相邻时有2 * (n-2)!种情况: 因为他们相邻,即排列组合中的捆绑法,其他n-2个点全排列是 (n-2)!,他们内部排列是 2!。而这样的相邻情况一共有n-1种,所以每个点对所有排列中的相邻次数是 2 * (n-1)!。
    对于上述的解释 :
    例如 有四个点 1 2 3 4,考虑 1 2点对相邻的情况
    在这里插入图片描述
    如图,四个位置,1 2点对在前两个位置时。1 2之间可以互换,其他两个位置全排列,所以一共 2 * (n-2)! = 2 * (4-2)!。
    而1 2点对可以在第二第三个位置,也可以在第三第四个位置,一共有n-1个位置,所以一共出现了 2 * (n-1)! = 2*3! = 12次。

    2)求出所有点对相邻的次数,再求出任意两点之间距离的和,再乘以次数即可。求任意两点距离和可以看这道题,树形dp的基础应用
    https://blog.csdn.net/qq_39763472/article/details/82894446

  • 代码 :

#include "bits/stdc++.h"
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define fori(i,l,u) for(int i = l;i < u;i++)
#define forj(j,l,u) for(int j = l;j < u;j++)
#define F first
#define S second
#define pb push_back
#define mk make_pair
typedef long long  ll;
typedef pair<int, int> pi;
typedef pair<string, int> ps;
typedef vector<int> vi;
typedef vector<string> vs;
typedef vector<pi> vpi;
const int maxn = 2e5 + 6;
const int INF = 1e9 + 7;
ll n;
ll x,y,w;
ll dp[maxn],sum[maxn];
typedef struct{
    ll v;
    ll w;
}node;
vector<node> tree[maxn];
void init(){
    mem(dp,0);
    mem(sum,0);
    fori(i, 1, n+1) tree[i].clear();
}
void dfs(ll cur,ll ft){
    sum[cur] = 1;
    fori(i, 0, tree[cur].size()){
        ll son = tree[cur][i].v;
        ll len = tree[cur][i].w;
        if (son == ft) {
            continue;
        }
        dfs(son, cur);
        sum[cur] += sum[son];
        dp[cur] = (dp[cur] + (dp[son] + sum[son] * (n-sum[son])%INF * len %INF) ) % INF;
    }
}
int main()
{
//    freopen("1.txt", "r", stdin);
    while(~scanf("%lld",&n)){
        init();
        fori(i, 1, n){
            scanf("%lld%lld%lld",&x,&y,&w);
            node t1,t2;
            t1.v = x;
            t1.w = w;
            t2.v = y;
            t2.w = w;
            tree[x].pb(t2);
            tree[y].pb(t1);
        }
        dfs(1, -1);
        ll num = 1;
        for (ll i = 1; i < n ; i++) {
            num = num * i % INF;
        }
        ll ans = 2 * dp[1] %INF* num %INF;
        printf("%lld\n",ans);
    }
    return 0;
}


  • 遇到的问题 :思路还是很清晰的,但是实现起来就有细节问题。
    1)还是scanf recommend,cin没试过,不过肯定wa。数据太大。
    2)第一次提交wa了,看到执行到1w k内存的时候wa的(这也是杭电的好处,如果现场赛就没法看出来了),所以肯定是大数据的时候出错,所以改成了所有的变量都是long long,但还是wa。突然想起来需要mod 1e9 + 7,但是需要的地方很多 :
    在这里插入图片描述
    首先是求(n-1)!需要mod,然后答案也需要mod,而且不能mod一次,因为中间结果也会超范围。
    然后是在这里插入图片描述
    树形dp中间结果,需要mod INF。sum因为是以某个节点为根的子树的节点数,所以最多1e5,len就是1e9的最大范围了,所以需要mod,dp也会超,所以相加需要mod。。因为mod问题wa了10次。。大家注意。。

猜你喜欢

转载自blog.csdn.net/qq_39763472/article/details/82940212
今日推荐