题解:P14270 ABC253Ex 加强版
P14270
尽量通俗一点。可能很多地方不太标准。
首先转边集计数为点集计数,记
计算
显然可以矩阵树定理,做到
我们按照点的编号从小到大加入,每次加入
假设当前连接的若干树的点集为
此时
显然,
即当前方案数为
设
不难发现,这个就是
对于每一个
引入:dp 计算多项式复合
考虑一个集合幂级数
现在我们要计算
定义集合
考虑第
可以考虑 dp,套路地定义子集加入顺序为按照
定义
转移考虑是否加入包含第
-
加入包含
i 的集合,设加入的集合为T\cup\{i\} 。则转移为f_{i,j,S\cup T\cup \{i\}}\leftarrow f_{i-1,j+1,S}\times g(T\cup\{i\}) -
不加入,则转移为
f_{i,j,S}\leftarrow f_{i-1,j,S}
第一种转移中
初始状态
用子集卷积转移,复杂度
这个做法叫逐点牛顿迭代法,但是我没看出哪里牛顿迭代。
::::success[Code]
inline void juan(int*A,int*B,int*C,int lim){
for(int i=0;i<=lim;i++)for(int S=0;S<(1<<lim);S++)P[i][S]=Q[i][S]=R[i][S]=0;
for(int S=0;S<(1<<lim);S++){
P[pc(S)][S]=A[S],Q[pc(S)][S]=B[S];
}
for(int i=0;i<=lim;i++)FWT(P[i],lim),FWT(Q[i],lim);
for(int S=0;S<(1<<lim);S++){
for(int i=0;i<=lim;i++)for(int j=0;i+j<=lim;j++){
(R[i+j][S]+=P[i][S]*Q[j][S]%mod)%=mod;
}
}
for(int i=0;i<=lim;i++)IWT(R[i],lim);
for(int S=0;S<(1<<lim);S++)C[S]=R[pc(S)][S];
}
int fac[MAXN];
inline void comp(int*G,int*F,int*Ans){
static int*f[MAXN][MAXN],vl[MAXS],tmp[MAXS];
for(int i=0;i<=n;i++)for(int j=0;j<=n-i;j++){
f[i][j]=new int[1<<i];
}
for(int i=0;i<=n;i++){
f[0][i][0]=G[i]*fac[i]%mod;
}
for(int i=1;i<=n;i++)for(int j=0;j<=n-i;j++){
for(int S=0;S<(1<<i-1);S++)vl[S]=F[S|(1<<i-1)];
juan(vl,f[i-1][j+1],tmp,i-1);
for(int S=0;S<(1<<i-1);S++){
f[i][j][S]=f[i-1][j][S];
f[i][j][S|(1<<i-1)]=tmp[S];
}
}
for(int S=0;S<(1<<n);S++)Ans[S]=f[n][0][S];
}
::::
重设计 dp
现在看回这道题。加入
考虑上加边顺序的方案数后,加入
所以只需考虑
对于刚才那个 dp,最后需要记录当前集合,但是此时不关心
对于现在的 dp,最后关心
发现完全就是相反的,我们把 dp 倒过来做一遍就可以了。
状态定义改为:当前考虑到第
转移类似,每次枚举
初始值
此时子集卷积变成差卷积,只需要取反卷积中两个集合,再正常子集卷积即可,具体可以参考代码。
复杂度也是
Code
可以证明上面提到的复杂度均为
常数较大,看看就好。题解中 dp 数组名字用了
::::success[Code]
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=20+2,MAXS=(1<<20)+5,mod=998244353;
#define lb(x) (x&-x)
int n,m,E[MAXN][MAXN],pc[MAXS],inv[MAXN],fac[MAXN];
inline void FWT(int*A,int lim){
for(int mid=1;mid<(1<<lim);mid<<=1){
for(int R=mid<<1,i=0;i<(1<<lim);i+=R){
for(int j=0;j<mid;j++)(A[i|j|mid]+=A[i|j])%=mod;
}
}
}
inline void IWT(int*A,int lim){
for(int mid=1;mid<(1<<lim);mid<<=1){
for(int R=mid<<1,i=0;i<(1<<lim);i+=R){
for(int j=0;j<mid;j++)(A[i|j|mid]+=mod-A[i|j])%=mod;
}
}
}
int P[MAXN][MAXS],Q[MAXN][MAXS],R[MAXN][MAXS];
inline void Exp(int*A,int*B,int lim){
for(int i=0;i<=lim;i++)for(int S=0;S<(1<<lim);S++)P[i][S]=Q[i][S]=0;
for(int S=0;S<(1<<lim);S++)P[pc[S]][S]=A[S];
for(int i=0;i<=lim;i++)FWT(P[i],lim);
for(int S=0;S<(1<<lim);S++){
Q[0][S]=1;
for(int i=1;i<=lim;i++)for(int j=1;j<=i;j++){
(Q[i][S]+=inv[i]*j%mod*P[j][S]%mod*Q[i-j][S]%mod)%=mod;
}
}
for(int i=0;i<=lim;i++)IWT(Q[i],lim);
for(int S=0;S<(1<<lim);S++)B[S]=Q[pc[S]][S];
}
int f[MAXS],tmp[MAXS],lin[MAXS];
int*H[MAXN][MAXN],vl[MAXS];
inline void juan(int*A,int*B,int*C,int lim){
int U=(1<<lim)-1;
for(int i=0;i<=lim;i++)for(int S=0;S<(1<<lim);S++)P[i][S]=Q[i][S]=R[i][S]=0;
for(int S=0;S<(1<<lim);S++){
P[pc[S]][S]=A[U^S],Q[pc[S]][S]=B[S];
}
for(int i=0;i<=lim;i++)FWT(P[i],lim),FWT(Q[i],lim);
for(int S=0;S<(1<<lim);S++){
for(int i=0;i<=lim;i++)for(int j=0;i+j<=lim;j++){
(R[i+j][S]+=P[i][S]*Q[j][S]%mod)%=mod;
}
}
for(int i=0;i<=lim;i++)IWT(R[i],lim);
for(int S=0;S<(1<<lim);S++)C[U^S]=R[pc[S]][S];
}
signed main(){
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;u--,v--;
E[u][v]++,E[v][u]++;
}
fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
inv[1]=1;for(int i=2;i<=n;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int S=1;S<(1<<n);S++)pc[S]=pc[S^lb(S)]+1;
f[1]=1;
for(int i=1;i<n;i++){
for(int S=0;S<(1<<i);S++){
int tim=0;
for(int j=0;j<i;j++)if(S>>j&1){
tim+=E[j][i];
}
tmp[S]=f[S]*tim%mod;
}
Exp(tmp,tmp,i);
for(int S=0;S<(1<<i);S++){
f[S|(1<<i)]=tmp[S];
}
}
for(int i=0;i<=n;i++)for(int j=0;j<=n-i;j++){
H[i][j]=new int[(1<<i)];
for(int S=0;S<(1<<i);S++)H[i][j][S]=0;
}
H[n][0][(1<<n)-1]=1;
for(int i=n-1;i>=0;i--)for(int j=0;j<=n-i;j++){
for(int S=0;S<(1<<i);S++)vl[S]=f[S|(1<<i)];
if(j){
for(int S=0;S<(1<<i);S++)lin[S]=H[i+1][j-1][S|(1<<i)];
juan(lin,vl,tmp,i);
}else{
for(int S=0;S<(1<<i);S++)tmp[S]=0;
}
for(int S=0;S<(1<<i);S++){
H[i][j][S]=tmp[S];
if(j!=n-i)(H[i][j][S]+=H[i+1][j][S])%=mod;
}
}
for(int i=1;i<n;i++)cout<<H[0][n-i][0]*fac[i]%mod<<"\n";
}
::::