题意
令 (G(n)) 为 (n) 个点的无向简单图的集合,(f(G)) 为 (G) 的所有极大连通块中树的个数。
求
[sum_{G(n)} f^k(G)
]
- (n le 10^4, k le 20, T le 100)
- (n le 5 imes 10^4, k le 20, T le 5 imes 10^5),(T) 为数据组数。
做法
首先考虑将 (k) 次幂拆成下降幂的形式:
[sum_{g in G(n)} sum_{i=1}^{f(g)} f^{underline{i}}(g) cdot egin{Bmatrix} k \ i end{Bmatrix}
]
改变求和的顺序
[sum_{i=1}^k egin{Bmatrix} k \ i end{Bmatrix} sum_{g in G(n)} f^{underline i}(g)
]
第二个求和号的组合意义就是在每个图中有序地选出 (k) 棵树的方案数,继续改变求和顺序,转为有序枚举 (k) 棵树,然后计算包含它们的图的总数。
设 (G) 为 (n) 点无向图的 EGF,(T) 为 (n) 点树的 EGF,因为要求 一定选出 (k) 颗树,所以这里 (T_0 = 0)。
设 (T_k = G imes T^k) 为强制选出选出 (k) 颗树的 EGF,带回原式乘上 Stirling 数的值即可。
求 (T_k) 的复杂度:(mathcal O(kn log n))
带回求答案的复杂度:(mathcal O(k^2 cdot n))
Code
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
#define File(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
typedef long long ll;
namespace io {
const int SIZE = (1 << 21) + 1;
char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
char getc () {return gc();}
inline void flush () {fwrite (obuf, 1, oS - obuf, stdout); oS = obuf;}
inline void putc (char x) {*oS ++ = x; if (oS == oT) flush ();}
template <class I> inline void gi (I &x) {for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;}
template <class I> inline void print (I x) {if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;while (x) qu[++ qr] = x % 10 + '0', x /= 10;while (qr) putc (qu[qr --]);}
struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi; using io :: putc; using io :: print; using io :: getc;
template<class T> void upmax(T &x, T y){x = x>y ? x : y;}
template<class T> void upmin(T &x, T y){x = x<y ? x : y;}
const int p = 998244353;
inline int add(int x, int y) { return x + y >= p ? x + y - p : x + y; }
inline int sub(int x, int y) { return x - y < 0 ? x - y + p : x - y; }
inline int mul(int x, int y) { return 1LL * x * y % p; }
inline void inc(int &x, int y=1) {x += y; if (x >= p) x -= p;}
inline void dec(int &x, int y=1) {x -= y; if(x < 0) x += p;}
inline int power(int x, int y){
int res = 1;
for(; y; y>>=1, x = mul(x, x)) if(y & 1) res = mul(res, x);
return res;
}
inline int inv(int x) { return power(x, p - 2); }
const int N = 262144, L = 262144, K = 22;
const int g = 3, ig = inv(g);
int Ni[N], Ki[N];
int n = 0, k = 0;
int f[K][N];
int W[L], iW[L];
int rev[L], last = 0;
int fac[N], ifac[N];
int S[K][K];
void prework(){
W[0] = iW[0] = 1;
W[1] = power(g, (p - 1) / L); iW[1] = inv(W[1]);
for(int i=2; i<(L>>1); i++){
W[i] = mul(W[i - 1], W[1]);
iW[i] = mul(iW[i - 1], iW[1]);
}
fac[0] = 1;
for(int i=1; i<=n; i++) fac[i] = mul(fac[i - 1], i);
ifac[n] = inv(fac[n]);
for(int i=n-1; i>=0; i--) ifac[i] = mul(ifac[i + 1], i + 1);
S[0][0] = 1;
for(int i=1; i<=20; i++)
for(int j=1; j<=i; j++)
S[i][j] = add(S[i - 1][j - 1], mul(j, S[i - 1][j]));
}
void init(int n){
if(last == n) return ;
last = n;
int lg = __builtin_ctz(n) - 1;
for(int i=1; i<n; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lg);
}
void NTT(int a[], int n, int w[]){
init(n);
for(int i=0; i<n; i++)
if(i < rev[i]) swap(a[i], a[rev[i]]);
for(int l=2, k=1; l<=n; l<<=1, k<<=1){
int wi = L / l;
for(int i=0; i<n; i+=l)
for(int j=i, li=i+k, wp=0; j<li; j++, wp+=wi){
int x = a[j], y = mul(a[j + k], w[wp]);
a[j] = add(x, y); a[j + k] = sub(x, y);
}
}
}
void work(){
prework();
static int T[L];
static int Tk[K][L];
int len = 1;
for(; len < n * 2 + 2; len <<= 1) ;
T[0] = 0; T[1] = 1;
Tk[0][0] = 1; Tk[0][1] = 1;
for(int i=2; i<=n; i++){
Tk[0][i] = mul(power(2, ((1LL * i * (i - 1)) >> 1) % (p - 1)), ifac[i]);
T[i] = mul(power(i, i - 2), ifac[i]);
}
int invlen = inv(len);
NTT(T, len, W);
static int tp[L];
for(int i=1; i<=k; i++){
copy_n(Tk[i - 1], len, tp);
NTT(tp, len, W);
for(int j=0; j<len; j++)
Tk[i][j] = mul(tp[j], T[j]);
NTT(Tk[i], len, iW);
for(int j=0; j<=n; j++)
Tk[i][j] = mul(Tk[i][j], invlen);
fill(Tk[i] + n + 1, Tk[i] + len, 0);
}
for(int i=1; i<=k; i++)
for(int j=1; j<=n; j++)
Tk[i][j] = mul(Tk[i][j], fac[j]);
for(int i=1; i<=k; i++)
for(int j=1; j<=i; j++)
for(int p=1; p<=n; p++)
inc(f[i][p], mul(Tk[j][p], S[i][j]));
}
int main(){
File("xuanyiming");
int T;
gi(T);
for(int i=0; i<T; i++){
gi(Ni[i]); gi(Ki[i]);
upmax(n, Ni[i]); upmax(k, Ki[i]);
}
work();
for(int i=0; i<T; i++)
print(f[Ki[i]][Ni[i]]), putc('
');
return 0;
}