Bracket Sequences on Tree【哈希+随机数+树DP】

题目链接 HDU-6647


  有一棵N个点构成的无根树,我们可以任选一点开始进行dfs跑,得到一个括号序,现在我们想知道跑完整个dfs,有多少种括号序?

  于是,可以比较容易的想到,我们可以推一个dp方程来解出从一个点出发,得到的方案数,假设dp[u]表示以u为子树的根节点时候的跑完下面子树的种类数,那么可以看到dp[u] = \frac{(degree[u] !)}{\prod a_i!} \cdot \prod dp[v],其中,a_i表示u的不同形态的子树的个数。

  于是,我们可以根据这个式子来进行DP得到目前假设的根节点的值,然后再根据该式子可以进行换根DP得到剩下以每个点为根时候的答案值,当然,当以它为根时候的树跟其他点为根的树是同构的时候,这时只需要算一个的答案就可以了。

  然后,这里的哈希值,我选用了随机数,因为一开始被卡了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define pii pair<int, int>
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
vector<int> Prime;
bool not_Prime[1300007] = {false};
void Prime_Init()
{
    for(int i=2; Prime.size() <= 100000 && i<1300000; i++)
    {
        if(!not_Prime[i])
        {
            Prime.push_back(i);
            for(int j = i * 2; j < 1300000; j += i) not_Prime[j] = true;
        }
    }
}
const ll mod = 998244353;
const int maxN = 1e5 + 7;
inline void MOD(ll &x) { x = x >= mod ? x % mod : x; }
inline ll mul(ll a, ll b) { a *= b; MOD(a); return a; }
inline ll qpow(ll a, ll b = mod - 2)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = mul(ans, a);
        b >>= 1;
        a = mul(a, a);
    }
    return ans;
}
ll jc[maxN], inv_jc[maxN];
int N;
namespace Graph
{
    int head[maxN], cnt;
    struct Eddge
    {
        int nex, to;
        Eddge(int a=-1, int b=0):nex(a), to(b) {}
    } edge[maxN << 1];
    inline void addEddge(int u, int v)
    {
        edge[cnt] = Eddge(head[u], v);
        head[u] = cnt++;
    }
    inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
    inline void init()
    {
        cnt = 0;
        for(int i=1; i<=N; i++) head[i] = -1;
    }
};
using namespace Graph;
unordered_map<ull, int> mp[maxN];
unordered_map<ull, int>::iterator it;
unordered_map<ull, bool> root_Tree;
uit siz[maxN], du[maxN];
ll dp[maxN], ans;
ull f[maxN];
void dfs(int u, int fa)
{
    siz[u] = 1;
    f[u] = 1;
    int num = 0;
    ll pi_dp_v = 1;
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        dfs(v, u);
        num++;
        f[u] += f[v] * Prime[siz[v]];
        siz[u] += siz[v];
        mp[u][f[v]]++;
        pi_dp_v = mul(pi_dp_v, dp[v]);
    }
    du[u] = num;
    dp[u] = mul(jc[num], pi_dp_v);
    for(it = mp[u].begin(); it != mp[u].end(); it++)
    {
        dp[u] = mul(dp[u], inv_jc[it->second]);
    }
}
void ex_dfs(int u, int fa, ull fa_f, ll fa_dp)
{
    ull now_f = f[u] + fa_f * Prime[N - siz[u]];
    ll now_dp = dp[u];
    now_dp = mul(now_dp, fa_dp);
    now_dp = mul(now_dp, mul(inv_jc[du[u]], jc[du[u] + 1]));
    now_dp = mul(now_dp, mul(jc[mp[u][fa_f]], inv_jc[mp[u][fa_f] + 1]));
    mp[u][fa_f]++;
    du[u]++;
    if(!root_Tree[now_f])
    {
        root_Tree[now_f] = true;
        ans += now_dp;
        MOD(ans);
    }
    ull nex_f; ll nex_dp;
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        nex_f = now_f - f[v] * Prime[siz[v]];
        nex_dp = mul(now_dp, qpow(dp[v]));
        nex_dp = mul(nex_dp, mul(inv_jc[du[u]], jc[du[u] - 1]));
        nex_dp = mul(nex_dp, mul(jc[mp[u][f[v]]], inv_jc[mp[u][f[v]] - 1]));
        ex_dfs(v, u, nex_f, nex_dp);
    }
}
inline void pre_Do()
{
    Prime_Init();
    for(int i=1, l, r; i<=100000; i++)
    {
        l = rand() % 100000 + 1; r = rand() % 100000 + 1;
        swap(Prime[l], Prime[r]);
    }
    jc[0] = 1;
    for(ll i = 1; i < maxN; i++) jc[i] = mul(i, jc[i - 1]);
    inv_jc[maxN - 1] = qpow(jc[maxN - 1]);
    for(int i = maxN - 2; i >= 0; i--) inv_jc[i] = mul(inv_jc[i + 1], i + 1);
}
int main()
{
    pre_Do();
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &N);
        root_Tree.clear();
        for(int i=1; i<=N; i++) mp[i].clear();
        init();
        for(int i=1, u, v; i<N; i++)
        {
            scanf("%d%d", &u, &v);
            _add(u, v);
        }
        dfs(1, 0);
        ans = dp[1];
        root_Tree[f[1]] = true;
        ll nex_dp = 0; ull nex_f = 0;
        for(int i=head[1], u; ~i; i=edge[i].nex)
        {
            u = edge[i].to;
            nex_f = f[1] - f[u] * Prime[siz[u]];
            nex_dp = mul(dp[1], qpow(dp[u]));
            nex_dp = mul(mul(nex_dp, inv_jc[du[1]]), jc[du[1] - 1]);
            nex_dp = mul(nex_dp, mul(jc[mp[1][f[u]]], inv_jc[mp[1][f[u]] - 1]));
            ex_dfs(u, 1, nex_f, nex_dp);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值