题解 LA4390

喜夏-厌秋 提交于 2020-02-12 16:58:31

题目大意 多组数据,每组数据给定两个个正整数 \(n, m\) 和一棵 \(n\) 个节点的树,输出给树标号使儿子的编号大于父亲的编号的方案数对 \(m\) 取模的值,不保证 \(m\) 是质数

分析 考虑这样一棵子树,它的所有根节点的大小 \(siz[son[i]]\) 和标号方案数 \(ans[son[i]]\) 都已知,那么可以得到,整棵树的方案数等于所有子树的方案数的乘积乘上对每个子树分配不同编号的总的方案数。换句话说,假如整棵树的编号为 \(1-tot\),则每个字树的编号对应着 \([2,tot]\) 中的 \(siz[son[i]]\) 个数,那么分配标号的方案数就是

\[\prod_{i=1}^{sonnum}C_{\sum_{j=i}^{n}siz[son[i]]}^{siz[son[i]]}\]

那么可以得到,总的方案数为

\[\prod_{i=1}^{sonnum}ans[son[i]]C_{\sum_{j=i}^{n}siz[son[i]]}^{siz[son[i]]}\]

值得注意的是 \(m\) 并非质数,所以需要用 exLucas 来处理组合数。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 5E+5 + 5;

int T, n, mod;
int fa[maxn], siz[maxn];
ll ans[maxn];
vector<int> son[maxn];

int tot;
ll a[100];
pair<ll, ll> p[100];

ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(!b) return x = 1, y = 0, a;
    
    ll d = exgcd(b, a % b, y, x);
    return y -= a / b * x, d;
}

ll Inv(ll x, ll m)
{
    ll y, res;
    exgcd(x, m, res, y);
    return res;
}

ll CRT()
{
    ll res = 0;
    for(int i = 1; i <= tot; ++i) {
        ll w = mod / p[i].second, x, y;
        
        exgcd(w, p[i].second, x, y);
        res = (res + w * x * a[i]) % mod;
    }
    return (res + mod) % mod;
}

ll qPow(ll a, ll b, ll p)
{
    a = a % p;
    
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % p;
        a = a * a % p, b >>= 1;
    }
    return res;
}

ll f(ll n, int num)
{
    if(!n) return 1;
    
    ll pi = p[num].first, pk = p[num].second;
    
    ll x = 1, y = 1;
    for(int i = 1; i <= pk; ++i)
        if(i % pi) x = x * i % pk;
    x = qPow(x, n / pk, pk);
    
    for(int i = n - n % pk + 1; i <= n; ++i)
        if(i % pi) y = y * i % pk;
    
    return f(n / pi, num) * x % pk * y % pk;
}

ll g(ll n, ll p)
{
    if(n < p) return 0;
    return n / p + g(n / p, p);
}

ll exLucas(ll n, ll m)
{
    for(int i = 1; i <= tot; ++i) {
        a[i] = 0;
        
        ll pi = p[i].first, pk = p[i].second; 
        
        a[i] = f(n, i) * Inv(f(m, i), pk) % pk * Inv(f(n - m, i), pk) % pk;
        a[i] = a[i] * qPow(pi, g(n, pi) - g(m, pi) - g(n - m, pi), pk) % pk;
    }
    return CRT();
}

void Work(int num)
{
    ans[num] = 1, siz[num] = 1;
    for(int i = 0; i < son[num].size(); ++i)
        Work(son[num][i]), siz[num] += siz[son[num][i]];
    
    int tot = siz[num] - 1;
    for(int i = 0; i < son[num].size(); ++i)
        ans[num] = ans[num] * exLucas(tot, siz[son[num][i]]) % mod * ans[son[num][i]] % mod, tot -= siz[son[num][i]];
}

int main()
{
    scanf("%d", &T);
    while(T--) {
        tot = 0;
        
        scanf("%d%d", &n, &mod);
        for(int i = 1; i <= n; ++i)
            son[i].clear();
        
        int x = mod;
        for(int i = 2; i * i <= x; ++i) {
            if(x % i == 0) {
                p[++tot] = { i, 1 };
                while(x % i == 0) x /= i, p[tot].second *= i;
            }
        }
        if(x > 1) p[++tot] = { x, x };
        
        for(int i = 2; i <= n; ++i)
            scanf("%d", &fa[i]), son[fa[i]].push_back(i);
        
        Work(1);
        printf("%lld\n", ans[1]);
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!