Loj #6703 -「清华集训 2017」生成树计数

\[
\sum _{\sum v_i = n-2} \prod (a_i ^{v_i+1} * (v_i+1) ^ m /v_i!) *(\sum (v_i+1)^m)
\]

将 \(\sum (v_i+1)^m\) 中的贡献分开算。

我们有两个生成函数。

第一个:
\[
\sum _i a^{i+1} * (v_i+1)^m x^i / i!
\]
第二个:
\[
\sum _i a^{i+1} * (i+1)^{2m} x^i / i!
\]
先将 \(\prod a\) 算到前面。

令:
\[
A(x) =\sum x^i * (i+1)^{m} /i!
\B(x) =\sum x^i * (i+1)^{2m} /i!
\]
我们要算的是:
\[
\sum _i B(a_ix)/A(a_ix) * \prod A(a_jx)
\]
后面可以ln,exp。

然后发现泥算个等幂和就行了。

补充一下等幂和怎么算:
生成函数是:
\[
\sum \frac 1 {1-a_ix}
\]
因为
\[
\ln' (f(x)) = \frac {f'(x)}{f(x)}
\\ln' (1-a_ix) = \frac {-a_i} {1-a_ix} = -a_i - a_i^2 x
\]
所以可以一个分治FFT算。

最终复杂度$n\log ^2n $

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
typedef long long ll;

const int mod=998244353;
int add(int a,int b){a+=b;return a>=mod?a-mod:a;}
int sub(int a,int b){a-=b;return a<0?a+mod:a;}
int mul(int a,int b){return (ll)a*b%mod;}
int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
/*math*/

namespace Template_Poly{
    typedef vector<int> Poly;
    int rev[N];
    Poly Poly_add(Poly A,Poly B){
        A.resize(max(A.size(),B.size()));
        for(size_t i=0;i<B.size();i++)A[i]=add(A[i],B[i]);
        return A;
    }
    Poly Poly_sub(Poly A,Poly B){
        A.resize(max(A.size(),B.size()));
        for(size_t i=0;i<B.size();i++)A[i]=sub(A[i],B[i]);
        return A;
    }
    void DFT(int *t,int n,int type){
        int l=0;while(1<<l<n)++l;
        for(int i=0;i<n;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
        for(int i=0;i<n;i++)if(rev[i]>i)swap(t[rev[i]],t[i]);
        for(int step=1;step<n;step<<=1){
            int wn=qpow(3,(mod-1)/(step<<1));
            for(int i=0;i<n;i+=step<<1){
                int w=1;
                for(int k=0;k<step;k++,w=mul(w,wn)){
                    int x=t[i+k],y=mul(t[i+k+step],w);
                    t[i+k]=add(x,y),t[i+k+step]=sub(x,y);
                }
            }
        }
        if(type==1)return;
        for(int i=1;i<n-i;i++)swap(t[i],t[n-i]);
        int inv=qpow(n,mod-2);
        for(int i=0;i<n;i++)t[i]=mul(t[i],inv);
    }
    Poly NTT(Poly A,int n,Poly B,int m){
        static Poly res,PolA,PolB;
        PolA=A,PolB=B;
        int len=1;while(len < n+m)len<<=1;
        res.resize(len);
        PolA.resize(len),PolB.resize(len);
        DFT(&PolA[0],len,1);DFT(&PolB[0],len,1);
        for(int i=0;i<len;i++) res[i]= mul(PolA[i],PolB[i]);
        DFT(&res[0],len,-1);
        res.resize(n+m-1);
        return res;
    }
    Poly NTT(Poly A,Poly B){
        return NTT(A,A.size(),B,B.size());
    }
    Poly Poly_inv(Poly A,int n){
        if(n==1)return Poly(1,qpow(A[0],mod-2));
        int len=1<<((int)ceil(log2(n))+1);
        Poly x=Poly_inv(A,(n+1)>>1),y;
        x.resize(len),y.resize(len);
        for(int i=0;i<n;i++)y[i]=A[i];
        DFT(&x[0],len,1),DFT(&y[0],len,1);
        for(int i=0;i<len;i++)x[i]=mul(x[i],sub(2,mul(x[i],y[i])));
        DFT(&x[0],len,-1);
        x.resize(n);
        return x;
    }
    Poly Poly_inv(Poly A){
        return Poly_inv(A,A.size());
    }
    Poly Deri(Poly A){
        int n=A.size();
        for(int i=1;i<n;i++)A[i-1]=mul(A[i],i);
        A.resize(n-1);
        return A;
    }

    Poly Inte(Poly A){
        int n=A.size();
        A.resize(n+1);
        for(int i=n;i;i--)A[i]=mul(A[i-1],qpow(i,mod-2));
        A[0]=0;
        return A;
    }

    Poly ln(Poly A){
        int len=A.size();
        A=Inte(NTT(Deri(A),Poly_inv(A)));
        A.resize(len);
        return A;
    }

    Poly exp(Poly A,int n){
        if(n==1)return Poly(1,1);
        Poly x=exp(A,(n+1)>>1),y;
        x.resize(n);
        y=ln(x);
        for(int i=0;i<n;i++)y[i]=sub(A[i],y[i]);
        y[0]++;
        x=NTT(x,y);
        x.resize(n);
        return x;
    }
    Poly exp(Poly A){
        return exp(A,A.size());
    }

    Poly sqrt(Poly A,int n){
        if(n==1)return Poly(1,1);
        Poly x=sqrt(A,(n+1)>>1),y;
        x.resize(n),y.resize(n);
        for(int i=0;i<n;i++)y[i]=A[i];
        x=Poly_add(NTT(Poly_inv(x),y),x);
        int inv2=qpow(2,mod-2);
        for(int i=0;i<n;i++)
            x[i]=mul(x[i],inv2);
        x.resize(n);
        return x;
    }
    Poly sqrt(Poly A){
        return sqrt(A,A.size());
    }
    Poly rever(Poly A){
        reverse(A.begin(),A.end());
        return A;
    }
    void div(Poly A,Poly B,Poly &C,Poly &D){
        int n=A.size(),m=B.size();
        Poly ra=rever(A),rb=rever(B);
        ra.resize(n-m+1),rb.resize(n-m+1);
        C=NTT(ra,Poly_inv(rb));
        C.resize(n-m+1);
        C=rever(C);
        D=Poly_sub(A,NTT(B,C));
        D.resize(m);
    }
}
using namespace Template_Poly;
typedef Poly poly;
int n,m;
int a[N],fac[N],ifac[N];
inline void init(int n = 100010){
    fac[0]=ifac[0]=1;for(int i=1;i<=n;i++)fac[i] = mul(fac[i-1],i);
    ifac[n]=qpow(fac[n],mod-2);for(int i=n-1;i;i--)ifac[i] = mul(ifac[i+1],i+1);
}

poly solve(int l,int r){
    if(l==r){
        poly ret(2,1);
        ret[1]=sub(0,a[l]);
        return ret;
    }
    int mid=(l+r)>>1;
    return NTT(solve(l,mid),solve(mid+1,r));
}
poly func;
inline void calc(){
    func = solve(1,n);
    func = ln(func);
    // for(int i=0;i<func.size();i++)cout << func[i] << " ";puts("");
    func = Deri(func);
    func.push_back(0);
    for(int i=(int)func.size()-1;i;i--)func[i] = sub(0,func[i-1]);
    func[0]=n;
    // for(int i=0;i<func.size();i++)cout << func[i] << " ";puts("");
}
poly A,B,C;
poly ret;
void Doit(){
    A.resize(n-1),B.resize(n-1);
    for(int i=0;i<=n-2;i++){
        A[i] = mul(qpow(i+1, m),ifac[i]);
        B[i] = mul(qpow(i+1, 2*m),ifac[i]);
    }
    //A[0] = 1
    // B = Poly_inv(B);
    C = NTT(B,Poly_inv(A));
    C.resize(n-1);
    A = ln(A);
    for(int i=0;i<=n-2;i++){
        A[i] = mul(A[i], func[i]);
        C[i] = mul(C[i], func[i]);
    }
    A = exp(A);
    ret = NTT(A,C);
    int ans=ret[n-2];
    for(int i=1;i<=n;i++)ans=mul(ans,a[i]);
    ans = mul(ans, fac[n-2]);
    printf("%d\n",ans);
}

int main()
{
    init();
    cin >> n >> m;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    calc();
    Doit();
}

原文地址:https://www.cnblogs.com/weiyanpeng/p/12028247.html

时间: 2024-10-12 02:12:26

Loj #6703 -「清华集训 2017」生成树计数的相关文章

@loj - [email&#160;protected] 「清华集训 2017」生成树计数

目录 @[email protected] @[email protected] @正文@ @补充@ @accepted [email protected] @[email protected] @[email protected] 在一个 s 个点的图中,存在 s - n 条边,使图中形成了 n 个连通块,第 i 个连通块中有 \(a_i\) 个点. 现在我们需要再连接 n - 1 条边,使该图变成一棵树.对一种连边方案,设原图中第 i 个连通块连出了 \(d_i\) 条边,那么这棵树 T 的

[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主

[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主 试题描述 "A fight? Count me in!" 要打架了,算我一个. "Everyone, get in here!" 所有人,都过来! 小Y是一个喜欢玩游戏的OIer.一天,她正在玩一款游戏,要打一个Boss. 虽然这个Boss有 \(10^{100}\) 点生命值,但它只带了一个随从--一个只有 \(m\) 点生命值的"恐怖的奴隶主". 这个"恐怖的奴隶主&qu

[LOJ#2328]「清华集训 2017」避难所

[LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所了." "唔,那你之后会去哪呢?" "去一个没有冬天的地方." 对于一个正整数 \(n\),我们定义他在 \(b\) 进制下,各个位上的数的乘积为 \(p = F(n, b)\). 比如 \(F(3338, 10) = 216\). 考虑这样一个问题,已知 \

[LOJ#2327]「清华集训 2017」福若格斯

[LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最左边的两个格子上各有一个向右的青蛙,最右边的两个格子上各有一个向左的青蛙. 每次移动可以选取一个青蛙,向这只青蛙的前方移动一格到空格子中或跳过前方的一个不同朝向的青蛙并移动到空格子中. 为了使你更好地理解这个游戏,我们下发了一个游戏demo作为参考(注意:这个demo中的棋盘大小和题目中并不相同).

[LOJ#2331]「清华集训 2017」某位歌姬的故事

[LOJ#2331]「清华集训 2017」某位歌姬的故事 试题描述 IA是一名会唱歌的女孩子. IOI2018就要来了,IA决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符,第iii个音符的音高为 \(h_i\).IA的音域是 \(A\),她只能唱出 \(1\sim A\) 中的正整数音高.因此 \(1\le h_i\le A\). 在写歌之前,IA需要确定下这首歌的结构,于是她写下了 \(Q\) 条限制,其中第 \(i\) 条为:编号在 \(l_i\) 到 \(r_

loj2322 「清华集训 2017」Hello world!

https://loj.ac/problem/2322 先吐槽一下,sb数据毁我青春败我前程. 首先,一个数开根开不了多少次. 当我们把它开到1的时候,我们以后就不需要开他了,我们可以利用并查集跳过他,这是套路. 但是这个每次走$k$步,让人很头痛. 于是乎……分块 首先,对于$k$比较大的情况,我们可以暴力跳.否则, 我们对于每个$k$建一棵树,第$i$棵树上$x$的父亲是原树上$x$的第$i$个祖先 然后树剖,用线段树或者树状数组维护一下就好了. 然后!!打开榜,rk1,啥子,并查集+暴力跳

LibreOJ #2325. 「清华集训 2017」小Y和恐怖的奴隶主(矩阵快速幂优化DP)

哇这题剧毒,卡了好久常数才过T_T 设$f(i,s)$为到第$i$轮攻击,怪物状态为$s$时对boss的期望伤害,$sum$为状态$s$所表示的怪物个数,得到朴素的DP方程$f(i,s)=\sum \frac{1}{sum+1}*(f(i+1,s')+[s==s'])$ 状态数只有$C_{8+3}^3=165$个,所以就可以矩乘优化啦.再加上一个用于转移的$1$,矩阵大小是$166*166$的,因为多组询问,所以可以先把$2$的所有次幂的矩阵都预处理出来. 然后会发现复杂度是$O(T*166^3

「清华集训 2017」无限之环

无限之WA https://www.luogu.org/problemnew/show/P4003 本题如果知道是网络流的话,其实建图不算特别神奇,但是比较麻烦. 数据范围过大,插头dp不能处理,而且是一个网格图,考虑网络流. 先看是不是二分图? 每个格子只会和相邻四个格子发生关系 所以,黑白染色正好. i+j为偶数左部点,i+j为奇数右部点 不漏水是什么? 每个管道的四个口都能和别的接好. S向左,右向T连口数的容量,费用为0的边 考虑怎么能把左右连在一起. 考虑要上下左右四个方向匹配,那么必

「清华集训 2017」某位歌姬的故事

题目链接 问题分析 吐槽一下这个预处理比DP还长的题-- 首先对限制从小到大排序,然后不难发现对于每一种大小限制都是独立的.离散后考虑\(F[i][j]\)表示以\(i\)结尾,上一个音高为限制大小的位置\(j\)的方案种数.不难发现对于一些右端点相同的限制,左端点最右的限制才有效.这样就可以\(n^2\)动规了. 由于要离散化,所以细节很多. 参考程序 程序没有显式的离散化,并且大量使用结构体,所以又慢又长. #include <bits/stdc++.h> #define LL long