HDU - 5789 Permutation dp (看题解)

HDU - 5789

这个题真的是有点恶心了。

ans[ i ] 表示以 i 为根的子树的答案, 也就是所有合法排列的逆序对数量。

way[ i ] 表示以 i 为根的合法排列数量。

dp[ i ][ j ][ k ] 表示以 i 为根所有合法排列中 j 节点排在第 k 个的合法排列数量。

然后用这三个dp 数组去更新答案。

具体参考https://blog.csdn.net/ShinFeb/article/details/52212775

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define LL long long
#define LD long double
#define ull unsigned long long
#define fi first
#define se second
#define mk make_pair
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define fio ios::sync_with_stdio(false); cin.tie(0);

using namespace std;

const int N = 3e5 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = (int)1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);

template<class T, class S> inline void add(T& a, S b) {a += b; if(a >= mod) a -= mod;}
template<class T, class S> inline void sub(T& a, S b) {a -= b; if(a < 0) a += mod;}
template<class T, class S> inline bool chkmax(T& a, S b) {return a < b ? a = b, true : false;}
template<class T, class S> inline bool chkmin(T& a, S b) {return a > b ? a = b, true : false;}

int n, root;
int ans[51], way[51], sz[51];
int dp[51][51][51];
int tmp[51][51];
int sum[51][51];
int C[101][101];

vector<int> G[N];

void dfs(int u, int fa) {
    for(int i = 0; i < SZ(G[u]); i++) {
        if(G[u][i] == fa) {
            G[u].erase(G[u].begin() + i);
        }
    }
    ans[u] = 0; sz[u] = 1;
    for(auto &v : G[u]) {
        dfs(v, u);
        sz[u] += sz[v];
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            dp[u][i][j] = 0;
            sum[i][j] = 0;
        }
    }
    way[u] = 1;
    int cur_cnt = 0;
    for(auto &v : G[u]) {
        int nex_cnt = cur_cnt + sz[v];
        ans[u] = 1LL * C[nex_cnt][cur_cnt] * ans[u] % mod * way[v] % mod;
        add(ans[u], 1LL * C[nex_cnt][cur_cnt] * ans[v] % mod * way[u] % mod);
        for(int i = 1; i <= n; i++) {
            for(int k = 1; k <= sz[v]; k++) {
                if(!dp[v][i][k]) continue;
                for(int j = 0; j <= cur_cnt; j++) {
                    int a = 1LL * C[k + j - 1][j] * C[cur_cnt - j + sz[v] - k][cur_cnt - j] % mod;
                    int b = (sum[i][cur_cnt] - sum[i][j] + mod) % mod;
                    int c = (sum[n][j] - sum[i][j] + mod) % mod;
                    add(ans[u], 1LL * dp[v][i][k] * a % mod * (b + c) % mod);
                }
            }
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                tmp[i][j] = 0;
            }
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= nex_cnt; j++) {
                for(int k = 1; k <= j; k++) {
                    int a = 1LL * C[j - 1][k - 1] * C[cur_cnt + sz[v] - j][cur_cnt - k] % mod;
                    int b = 1LL * C[j - 1][k - 1] * C[cur_cnt + sz[v] - j][sz[v] - k] % mod;
                    add(tmp[i][j], 1LL * a * way[v] % mod * dp[u][i][k] % mod);
                    add(tmp[i][j], 1LL * b * way[u] % mod * dp[v][i][k] % mod);
                }
            }
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                dp[u][i][j] = tmp[i][j];
                sum[i][j] = dp[u][i][j];
                add(sum[i][j], sum[i - 1][j]);
                add(sum[i][j], sum[i][j - 1]);
                sub(sum[i][j], sum[i - 1][j - 1]);
            }
        }
        way[u] = 1LL * way[u] * way[v] % mod * C[nex_cnt][cur_cnt] % mod;
        cur_cnt = nex_cnt;
    }
    dp[u][u][sz[u]] = way[u];
    add(ans[u], sum[n][cur_cnt]);
    sub(ans[u], sum[u][cur_cnt]);
}

void init() {
    for(int i = 1; i <= n; i++) {
        G[i].clear();
    }
}

int main() {
    for(int i = 0; i <= 100; i++) {
        for(int j = C[i][0] = 1; j <= i; j++) {
            C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
            if(C[i][j] >= mod) C[i][j] -= mod;
        }
    }
    while(scanf("%d%d", &n, &root) != EOF) {
        init();
        for(int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(root, 0);
        printf("%d\n", ans[root]);
    }
    return 0;
}

/*
*/

 

转载于:https://www.cnblogs.com/CJLHY/p/11502975.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值