【LOJ】#6391. 「THUPC2018」淘米神的树 / Tommy

题解

一道非常神仙的计数题
如果只有一个点,就是非常简单的树型dp

\(f_{u} = (siz_{u} - 1)! \prod_{v \in son_{u}} \frac{f_{v}}{siz_{v}!}\)
\(\frac{f_{u}}{siz_{u}!} = \frac{1}{siz_{u}} \prod_{v \in son_{u}} \frac{f_{v}}{siz_{v}!}\)
\(f_{u} = \frac{n!}{\prod s_{i}}\)

可是我们有两个点,我们把这两个点连起来的点作为第一个黑点的话,这就是一个环套树,把环拎出来,我们枚举在哪里断开

我们设环长为m,添加上的黑点即\(v_0\)同时\(v_0\)\(v_m\)相连

这样的话,如果我们要让\(v_{i}\)是最后一个被染红的,那么不在环上的点的子树大小都不会改变
我们断开\(v_{i} - v_{i + 1}\)\(v_{i - 1} - v_{i}\)都可以
也就是说,我们断开一条边,对应着两个点被最后一个染色的方案数
总共计算了两遍,我们最后的答案除二就行

现在,我们把问题转化成了,枚举断开的一条边,把图变成一棵树,求这棵树的方案数,即套用我们所求的公式,需要的就是快速算出环上的点的子树大小更新的情况

我们用\(s_{i}\)表示第i个点上所挂的树的大小,并把这个东西处理成一个前缀和
我们对于每个点,它的值是
\(\prod_{i \neq j } (s_{i} - s_{j})\)
我们要求的是
\(\sum_{i} \prod_{i \neq q} (s_{i} - s_{j})\)
我们如果把\(s_{i}\)设成变量,那么
\(\sum_{i} \prod_{i \neq q} (x- s_{j})\)这个式子可以联系到导数
也就是
\(\sum_{i} \prod_{i \neq q} (x - s_{j}) = \frac{\mathrm{d} }{\mathrm{d} x} \prod (x - s_{i})\)
然后我们直接多项式插值就可以了

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <ctime>
#include <map>
#include <set>
#define fi first
#define se second
#define pii pair<int,int>
//#define ivorysi
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define MAXN 234600
using namespace std;
typedef long long int64;
typedef double db;
typedef unsigned int u32;
template<class T>
void read(T &res) {
    res = 0;T f = 1;char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9' ) {
        res = res * 10 - '0' + c;
        c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
        out(x / 10);
    }
    putchar('0' + x % 10);
}

const int MOD = 998244353;
int W[(1 << 20) + 5];
int mul(int a,int b) {
    return 1LL * a * b % MOD;
}
int inc(int a,int b) {
    return a + b >= MOD ? a + b - MOD : a + b;
}
int fpow(int x,int c) {
    int res = 1,t = x;
    while(c) {
        if(c & 1) res = mul(res,t);
        t = mul(t,t);
        c >>= 1;
    }
    return res;
}
struct node {
    int to,next;
}E[MAXN * 2];
int head[MAXN],sumE,N,A,B,siz[MAXN],fa[MAXN],sum[MAXN],tot,fac,inv[MAXN];
int ans[MAXN];
void add(int u,int v) {
    E[++sumE].to = v;
    E[sumE].next = head[u];
    head[u] = sumE;
}
void dfs(int u) {
    siz[u] = 1;
    for(int i = head[u] ; i ; i = E[i].next) {
        int v = E[i].to;
        if(v != fa[u]) {
            fa[v] = u;
            dfs(v);
            siz[u] += siz[v];
        }
    }
}
struct poly {
    vector<int> p;
    poly() {
        p.clear();
    }
    void print() {
        for(int i = 0 ; i < p.size() ; ++i) {out(p[i]);space;}
        enter;
    }
    friend void NTT(poly &f,int L,int on) {
        f.p.resize(L);
        for(int i = 1, j = L / 2 ; i < L - 1 ; ++i) {
            if(i < j) swap(f.p[i],f.p[j]);
            int k = L / 2;
            while(j >= k) {
                j -= k;
                k >>= 1;
            }
            j += k;
        }
        for(int h = 2 ; h <= L ; h <<= 1) {
            int wn = W[((1 << 20) + on * (1 << 20) / h) % (1 << 20)];
            for(int k = 0 ; k < L ; k += h) {
                int w = 1;
                for(int j = k ; j < k + h / 2 ; ++j) {
                    int u = f.p[j],t = mul(w,f.p[j + h / 2]);
                    f.p[j] = inc(u,t);
                    f.p[j + h / 2] = inc(u,MOD - t);
                    w = mul(w,wn);
                }
            }
        }
        if(on == -1) {
            int InvL = fpow(L,MOD - 2);
            for(int i = 0 ; i < L ; ++i) {
                f.p[i] = mul(f.p[i],InvL);
            }
        }
    }
    friend poly operator * (poly f,poly g) {
        int L = f.p.size() + g.p.size();
        int t = 1;
        while(t <= L) t <<= 1;
        poly h;h.p.resize(t);
        NTT(f,t,1);NTT(g,t,1);
        for(int i = 0 ; i < t ; ++i) {
            h.p[i] = mul(f.p[i],g.p[i]);
        }
        NTT(h,t,-1);
        for(int i = t - 1 ; i >= 0; --i) {
            if(h.p[i] == 0) h.p.pop_back();
            else break;
        }
        return h;
    }
    friend poly operator + (poly f,poly g) {
        poly h;
        int t = max(f.p.size(),g.p.size());
        f.p.resize(t);g.p.resize(t);
        for(int i = 0 ; i < t ; ++i) {
            h.p.pb(inc(f.p[i],g.p[i]));
        }
        for(int i = t - 1; i >= 0 ; --i) {
            if(!h.p[i]) h.p.pop_back();
            else break;
        }
        return h;
    }
    friend poly operator - (poly f,poly g) {
        poly h;
        int t = max(f.p.size(),g.p.size());
        f.p.resize(t);g.p.resize(t);
        for(int i = 0 ; i < t ; ++i) {
            h.p.pb(inc(f.p[i],MOD - g.p[i]));
        }
        for(int i = t - 1; i >= 0 ; --i) {
            if(!h.p[i]) h.p.pop_back();
            else break;
        }
        return h;
    }
    friend poly Inverse(poly f,int L) {
        poly g,r,two;
        two.p.pb(2);
        g.p.pb(fpow(f.p[0],MOD - 2));r.p.pb(f.p[0]);
        int t = 1;
        while(t <= L) {
            int m = min(t * 2,(int)f.p.size());
            for(int i = t ; i < m ; ++i) {
                r.p.pb(f.p[i]);
            }
            t = t * 2;
            g = g * (two - r * g);
            int tmp = g.p.size();
            for(int i = tmp - 1; i >= t ; --i) g.p.pop_back();
        }
        t = g.p.size();
        for(int i = t - 1 ; i >= L ; --i) g.p.pop_back();
        t = L - 1;
        for(int i = t ; i >= 0 ; --i) {
            if(!g.p[i]) g.p.pop_back();
            else break;
        }
        return g;
    }
    friend poly operator / (poly f,poly g) {
        reverse(f.p.begin(),f.p.end());
        reverse(g.p.begin(),g.p.end());
        int t = f.p.size() - g.p.size() + 1;
        poly h = Inverse(g,t);
        poly q = f * h;
        for(int i = q.p.size() - 1 ; i >= t ; --i) q.p.pop_back();
        reverse(q.p.begin(),q.p.end());
        for(int i = t - 1 ; i >= 0 ; --i) {
            if(!q.p[i]) q.p.pop_back();
            else break;
        }

        return q;
    }
    friend poly operator % (poly f,poly g) {
        return f - g * (f / g);
    }
};
poly tr[MAXN * 4];
poly Solve1(int u,int l,int r) {
    if(l == r) {
        tr[u].p.pb(MOD - sum[l]);tr[u].p.pb(1);
        return tr[u];
    }
    int mid = (l + r) >> 1;
    tr[u << 1] = Solve1(u << 1,l,mid);
    tr[u << 1 | 1] = Solve1(u << 1 | 1,mid + 1,r);
    return tr[u] = tr[u << 1] * tr[u << 1 | 1];
}
void Solve2(int u,int l,int r,poly f) {
    if(l == r) {
        if(!f.p.size()) ans[l] = 0;
        else ans[l] = f.p[0];
        return;
    }
    int mid = (l + r) >> 1;
    Solve2(u << 1,l,mid,f % tr[u << 1]);
    Solve2(u << 1 | 1,mid + 1,r,f % tr[u << 1 | 1]);
}
void Init() {
    W[0] = 1;
    W[1] = fpow(3,(MOD - 1) / (1 << 20));
    for(int i = 2 ; i < (1 << 20) ; ++i) {
        W[i] = mul(W[i - 1],W[1]);
    }
    read(N);read(A);read(B);
    int x,y;
    for(int i = 1 ; i < N ; ++i) {
        read(x);read(y);
        add(x,y);add(y,x);
    }
    dfs(A);
    int f = B,t = 0;
    fac = 1;
    while(t != A) {
        sum[++tot] = siz[f] - siz[t];
        fac = mul(fac,siz[f]);
        t = f;
        f = fa[f];
    }
    for(int i = 1 ; i <= tot ; ++i) sum[i] += sum[i - 1];
    inv[1] = 1;
    for(int i = 2 ; i <= N; ++i) {
        inv[i] = mul(inv[MOD % i],MOD - MOD / i);
        fac = mul(fac,i);
    }
    for(int i = 1 ; i <= N ; ++i) fac = mul(fac,inv[siz[i]]);
    
}
void Solve() {
    poly f = Solve1(1,0,tot);
    int t = f.p.size();
    for(int i = 0 ; i < t - 1 ; ++i) {
        f.p[i] = mul(f.p[i + 1],i + 1);
    }
    f.p.pop_back();
    
    Solve2(1,0,tot,f);
    int tmp = 1;
    for(int i = tot ; i >= 0 ; --i) {
        ans[i] = mul(ans[i],tmp);
        tmp = mul(tmp,MOD - 1);
    }
    tmp = 0;
    for(int i = 0 ; i <= tot ; ++i) {
        tmp = inc(tmp,fpow(ans[i],MOD - 2));
    }
    fac = mul(fac,tmp);
    fac = mul(fac,inv[2]);
    out(fac);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Init();
    Solve();
    return 0;
}

转载于:https://www.cnblogs.com/ivorysi/p/9410641.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值