【题解】CodeChef - TREDEG (prufer+生成函数+多项式exp)

【题解】CodeChef - TREDEG (prufer+生成函数+多项式exp)

好毒瘤的数据范围...

先转prufer,现在问题就变成了我要生成一个\(n-2\)长度的序列,每一种序列的权值定义为每种数的\(\prod\)(每种数出现个数+1),可以直接使用指数型生成函数生成,具体的:
\[
(\sum_{i=0} {i+1\over i!}x^i)^n[x^{n-2}](n-2)!
\]
这个就生成这个序列的答案了。用exp搞个快速幂就完事了。

最终答案的式子
\[
(\sum_{i=0} {i+1\over i!}x^i)^n[x^{n-2}](n-2)!\over n^{n-2}
\]

然后数据范围要我们单独做\(k=1\),那么把\((\sum_{i=0} {i+1\over i!}x^i)^n[x^{n-2}]\)单独拿出来
\[
(\sum_{i=0} {i+1\over i!}x^i)^n[x^{n-2}]
\]
用\(e^x\)代替
\[
(xe^x+e^x)^n[x^{n-2}]
\]
二项式定理展开
\[
[x^{n-2}]\sum {n\choose i} x^ie^{ix}e^{(n-i)x}
\]
合并
\[
[x^{n-2}]\sum {n\choose i} x^ie^{nx}
\]
化简一下
\[
\sum {n\choose i} e^{nx}[x^{n-2-i}]
\]
泰勒展开一下
\[
\sum {n\choose i} {n^{n-2-i}\over (n-2-i)!}
\]
就可以\(O(n)\)算了。

代码:(很长)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
//#define getchar() (__c==__ed?(__ed=__buf+fread(__c=__buf,1,1<<18,stdin),*__c++):*__c++)

using namespace std;  typedef long long ll;   char __buf[1<<18],*__c=__buf,*__ed=__buf;
inline int qr(){
    int ret=0,f=0,c=getchar();
    while(!isdigit(c)) f|=c==45,c=getchar();
    while( isdigit(c)) ret=ret*10+c-48,c=getchar();
    return f?-ret:ret;
}

const int maxn=1<<22;
const int mod=998244353;
const int g=3;
const int gi=(mod+1)/3;
inline int MOD(const int&x){return x>=mod?x-mod:x;}
inline int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
int invs[maxn],jc[maxn],inv[maxn];
int ksm(const int&ba,const int&p){
    int ret=1;
    for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
        if(t&1) ret=MOD(ret,b);
    return ret;
}
void NTT(int*a,const int&len,const int&tag){
    static int r[maxn];
    for(int t=1;t<len;++t)
        if((r[t]=r[t>>1]>>1|(t&1?len>>1:0))>t)
            swap(a[t],a[r[t]]);
    for(int t=1,s=tag==1?g:gi,wn;t<len;t<<=1){
        wn=ksm(s,(mod-1)/(t<<1));
        for(int i=0;i<len;i+=t<<1)
            for(int j=0,w=1,p;j<t;++j,w=MOD(w,wn))
                p=MOD(a[i+j+t],w),a[i+j+t]=MOD(a[i+j]-p+mod),a[i+j]=MOD(a[i+j]+p);
    }
    if(tag!=1)
        for(int t=0,i=mod-(mod-1)/len;t<len;++t)
            a[t]=MOD(a[t],i);
}
void Deri(int*a,const int&len){
    for(int t=0;t<len-1;++t) a[t]=MOD(a[t+1],t+1);
    a[len-1]=0;
}
void Inter(int*a,int*b,const int&len){
    for(int t=len-1;t;--t) b[t]=MOD(a[t-1],invs[t]);
    b[0]=0;
}
void INV(int*a,int*b,const int&len){
    if(len==1) return b[0]=ksm(a[0],mod-2),void();
    INV(a,b,len>>1);
    static int A[maxn],B[maxn];
    memset(A,0,len<<3); memset(B,0,len<<3);
    memcpy(A,a,len<<2); memcpy(B,b,len<<2);
    NTT(A,len<<1,1); NTT(B,len<<1,1);
    for(int t=0;t<len<<1;++t) A[t]=MOD(B[t],MOD(A[t],B[t]));
    NTT(A,len<<1,0);
    for(int t=0;t<len;++t) b[t]=MOD(MOD(b[t]+b[t])-A[t]+mod);
}

void LN(int*a,int*b,const int&len){
    static int A[maxn],B[maxn];
    memset(A,0,len<<3); memset(B,0,len<<3);
    memcpy(A,a,len<<2);
    INV(A,B,len); Deri(A,len);
    NTT(A,len<<1,1); NTT(B,len<<1,1);
    for(int t=0;t<len<<1;++t) A[t]=MOD(A[t],B[t]);
    NTT(A,len<<1,0);
    Inter(A,b,len);
}

void EXP(int*a,int*b,const int&len){
    if(len==1){b[0]=1;return;}
    EXP(a,b,len>>1);
    static int A[maxn],B[maxn];
    memset(A,0,len<<3); memset(B,0,len<<3);
    memcpy(A,b,len<<1); LN(b,B,len);
    for(int t=0;t<len;++t) B[t]=MOD(a[t]-B[t]+mod);
    ++B[0];
    NTT(A,len<<1,1); NTT(B,len<<1,1);
    for(int t=0;t<len<<1;++t) A[t]=MOD(A[t],B[t]);
    NTT(A,len<<1,0);
    for(int t=0;t<len;++t) b[t]=A[t];
}

void POW(int*a,int*b,const int&len,const int&k){
    static int A[maxn],B[maxn];
    memset(A,0,len<<3); memcpy(A,a,len<<2);
    memset(B,0,len<<3);
    LN(A,A,len);
    for(int t=0;t<len;++t) A[t]=MOD(A[t],k);
    EXP(A,B,len);
    memcpy(b,B,len<<2);
}

void pre(const int&n){
    jc[0]=invs[1]=inv[0]=1;
    for(int t=1;t<=n;++t) jc[t]=MOD(jc[t-1],t);
    for(int t=2;t<=n;++t) invs[t]=MOD(mod-mod/t,invs[mod%t]);
    for(int t=1;t<=n;++t) inv[t]=MOD(inv[t-1],invs[t]);
    //for(int t=0;t<=n;++t) if(MOD(jc[t],inv[t])!=1) puts("wa"),cerr<<"t="<<t<<endl;
}

int c(const int&n,const int&m){
    if(n<m) return 0;
    return MOD(jc[n],MOD(inv[m],inv[n-m]));
}

int main(){
    pre(maxn-1);
#ifdef debug
    static int test[maxn],tesv[maxn];
    for(int t=0;t<4;++t) test[t]=t+1;
    NTT(test,8,1);
    NTT(test,8,0);
    for(int t=0;t<16;++t) fprintf(stderr,"%d%c",test[t]," \n"[t==15]);
    INV(test,tesv,4);
    NTT(test,8,1); NTT(tesv,8,1);
    for(int t=0;t<8;++t) test[t]=MOD(test[t],tesv[t]);
    NTT(test,8,0);
    for(int t=0;t<8;++t) fprintf(stderr,"%d%c",test[t]," \n"[t==7]);
    for(int t=0;t<8;++t) test[t]=0;
    for(int t=0;t<4;++t) test[t]=t+1;
    Deri(test,4);
    for(int t=0;t<8;++t) fprintf(stderr,"%d%c",test[t]," \n"[t==7]);
    Inter(test,test,4);
    Deri(test,4);
    for(int t=0;t<8;++t) fprintf(stderr,"%d%c",test[t]," \n"[t==7]);
    for(int t=0;t<8;++t) test[t]=0;
    for(int t=0;t<4;++t) test[t]=t+1;
    POW(test,test,8,2);
    for(int t=0;t<8;++t) fprintf(stderr,"%d%c",test[t]," \n"[t==7]);
#endif
    int T=qr();
    while(T--){
        int n=qr(),k=qr();
        if(k!=1){
            static int a[maxn];
            int len=1;
            while(len<=n) len<<=1;
            memset(a,0,len<<2);
            for(int t=0;t<=n;++t) a[t]=MOD(inv[t],ksm(t+1,k));
            POW(a,a,len,n);
            //cerr<<"a[n-2]="<<a[n-2]<<endl;
            int ans=MOD(MOD(a[n-2],jc[n-2]),ksm(ksm(n,n-2),mod-2));
            printf("%d\n",ans);
        }else{
            int ans=0;
            for(int t=0;t<=n-2;++t)
                ans=MOD(ans+MOD(c(n,t),MOD(ksm(n,n-2-t),inv[n-2-t])));
            ans=MOD(ans,MOD(jc[n-2],ksm(ksm(n,n-2),mod-2)));
            printf("%d\n",ans);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/winlere/p/12244077.html

时间: 2025-01-07 10:39:21

【题解】CodeChef - TREDEG (prufer+生成函数+多项式exp)的相关文章

luoguP5860 「SWTR-03」Counting Trees 生成函数+多项式exp

有 $n$ 个点,每个点有一个度数 $v[i]$,代表如果选择这个点就必须满足这个点与 $v[i]$ 条边相连. 求:有多少种选法,使得所选集合中的点能构成一棵树. 如果 $m$ 个点能生成一颗树,那么一定满足 $\sum v_{i}=2\times (m-1)$ 这是因为度数之和其实就是边的数量 $\times 2$. ~~然后感性理解~~:如果 $m$ 个点满足 $\sum v_{i}=2\times(m-1)$,则一定能构成一棵树. 那就将问题转化成:有 $m$ 个物品,每个物品的价值为

【WC2019】数树 树形DP 多项式exp

题目大意 有两棵 \(n\) 个点的树 \(T_1\) 和 \(T_2\). 你要给每个点一个权值吗,要求每个点的权值为 \([1,y]\) 内的整数. 对于一条同时出现在两棵树上的边,这条边的两个端点的值相同. 若 \(op=0\),则给你两棵树 \(T_1,T_2\),求方案数. 若 \(op=1\),则给你一棵树 \(T_1\),求对于所有 \(n^{n-2}\) 种 \(T_2\),方案数之和. 若 \(op=2\),则求对于所有的 \(T_1,T_2\),求方案数之和. \(n\leq

多项式exp

写了一发多项式exp,picks太神辣 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 const int N = 262144,mod = 998244353,G = 3; 7 8 void read(int&x){ 9 x = 0;char ch = getchar(); 10 while (ch < '0' || '9' < ch) ch = getchar

【XSY2744】信仰圣光 分治FFT 多项式exp 容斥原理

题目描述 有一个\(n\)个元素的置换,你要选择\(k\)个元素,问有多少种方案满足:对于每个轮换,你都选择了其中的一个元素. 对\(998244353\)取模. \(k\leq n\leq 152501\) 题解 吐槽 为什么一道FFT题要把\(n\)设为\(150000\)? 解法一 先把轮换拆出来. 直接DP. 设\(f_{i,j}\)为前\(i\)个轮换选择了\(j\)个元素,且每个轮换都选择了至少一个元素的方案数. \[ f_{i,j}=\sum_{k=1}^{a_i}f_{i-1,j

codechef : TREDEG , Trees and Degrees

其实有原题,生成树计数 然鹅这题里面是两道题, 50pts 可以用上面那题的做法直接过掉,另外 50pts 要推推式子,搞出 O n 的做法才行(毕竟多项式常数之大您是知道的) 虽说这道题里面是没有 a_i 的,也不用分治合并多项式的就是了,所以大致思路看我另一题的题解就好了,这里对于前 50pts 的做法只给出式子: \[ANS_n= {(n-2)! \Big( [x^{n-2}] \big(\sum_{i=0}^\infty (i+1) ^m {x^i \over i! } \big)^n

[BZOJ3456]城市规划(生成函数+多项式求逆+多项式求ln)

城市规划 时间限制:40s      空间限制:256MB 题目描述 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.  刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.  好了, 这就

BZOJ3992 [SDOI2015]序列统计 【生成函数 + 多项式快速幂】

题目 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数 列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助: 给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个.小C认为 ,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi.另外,小C认为这个问题的答案可能很大 ,因此他只需要你帮助他求出答案mod 1004535809

CF438E The Child and Binary Tree(生成函数+多项式开根+多项式求逆)

传送门 可以……这很多项式开根模板……而且也完全不知道大佬们怎么把这题的式子推出来的…… 首先,这题需要多项式开根和多项式求逆.多项式求逆看这里->这里,这里讲一讲多项式开根 多项式开方:已知多项式$A$,求多项式$B$满足$A^2\equiv B\pmod{x^n}$(和多项式求逆一样这里需要取模,否则$A$可能会有无数项) 假设我们已经求出$A'^2\equiv B\pmod{x^n}$,考虑如何计算出$A^2\equiv B\pmod{x^{2n}}$ 首先肯定存在$A^2\equiv B

Luogu5162 WD与积木(生成函数+多项式求逆)

显然的做法是求出斯特林数,但没有什么优化空间. 考虑一种暴力dp,即设f[i]为i块积木的所有方案层数之和,g[i]为i块积木的方案数.转移时枚举第一层是哪些积木,于是有f[i]=g[i]+ΣC(i,j)·f[i-j],g[i]=ΣC(i,j)·g[i-j] (j=1~i). 考虑优化 .我们发现这个转移非常像卷积.写成卷积形式,有f[i]=g[i]+Σi!·Σf[i-j]/j!/(i-j)!,g[i]=i!·Σg[i-j]/j!/(i-j)!.直接分治NTT即可. 诶是不是强行多了个log?考