统计带标号 n 个点可以组成多少种不同的有环连通图,答案
对质数 ( 2 15 × 4641 + 1 ) (2^{15} × 4641 + 1) (215×4641+1) 取模,其中每条边可以染成 m m m 种颜
色
考虑算出联通图的方案数,再减去树的方案个数。
1. n n n个点树的方案个数为 n n − 2 n^{n-2} nn−2
2.考虑任意图的指数生成函数
G ( x ) = ∑ n ≥ 0 ( m + 1 ) ( n 2 ) n ! x n G(x) = \sum_{n \ge 0} \frac {(m+1)^{\binom{n}{2}}}{n!} x^n G(x)=∑n≥0n!(m+1)(2n)xn,即对所有的边考虑染色.
3. 设连通图的指数生成函数为 F ( x ) F(x) F(x)
G ( x ) = ∑ k ≥ 1 F k ( x ) k ! = exp ( F ( x ) ) G(x) = \sum_{k \ge1} \frac{F_k(x)}{k!} = \exp(F(x)) G(x)=∑k≥1k!Fk(x)=exp(F(x))
4.对 G ( x ) G(x) G(x)求对数即可,详情见 2015 2015 2015金策论文
#include
using namespace std;
typedef long long ll;
const int maxn = 4e5+10;
//不声明为常量慢一倍
const int P = 152076289;
const int mod = 152076289;
const ll g = 127;
void Add(ll& x,ll y){
x += y;
if(x>=mod) x-= mod;
}
void Mul(ll& x,ll y){
x *= y;
if(x>=mod) x%=mod;
}
ll qpow(ll a,ll b){
ll ret = 1;
while(b){
if(b&1) Mul(ret,a);
b >>= 1;
Mul(a,a);
}
return ret;
}
inline ll Inv(ll a){
return qpow(a,mod-2);
}
ll inv[maxn],fac[maxn],finv[maxn];
void init(){
inv[0] = inv[1] = fac[0] = fac[1] = 1;
for(int i = 2;i<maxn;i++) inv[i] = inv[P % i] * (P - P/i) % P;
for(int i = 2;i<maxn;i++) fac[i] = fac[i - 1] * i % mod;
finv[0] = finv[1] = 1;
for(int i = 2;i<maxn;i++) finv[i] = finv[i-1] * inv[i] % mod;
}
struct NTT{
int rev[maxn],N,L;
void init_rev(int n){
for(N = 1,L=0;N<=n;N<<=1,L++);
for(int i=1;i<N;i++) rev[i] = (rev[i >> 1] >> 1 | ((i&1) << (L-1)));
}
void DFT(ll a[],int flag){
for(int i = 0;i<N;i++)
if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int l = 2;l<=N;l <<=1 ){
ll wn;
if(flag == 1) wn = qpow(g,(P-1)/l);
else wn = qpow(g,P-1-(P-1)/l);
for(int k = 0;k<N;k += l){
ll w = 1;
ll x,y;
for(int j = k;j < k + l/2 ;j++){
x = a[j];
y = a[j + l/2] * w % P;
a[j] = (x + y) % P;
a[j + l/2] = (x - y +P) % P;
Mul(w,wn);
}
}
}
if(flag == -1){
ll inv = Inv(N);
for(int i = 0; i<N ;i++) a[i] = a[i] * inv % P;
}
}
}ntt;
ll A[maxn],B[maxn],C[maxn],D[maxn];
ll f[maxn],G[maxn];
void poly_inv(int deg,ll* a,ll * b){
if(deg == 1){
b[0] = Inv(a[0]);
return ;
}
poly_inv((deg+1)>>1,a,b); ntt.init_rev(deg<<1);
for(int i = 0;i<deg;i++) C[i] = a[i];
for(int i = deg;i<ntt.N;i++) C[i] = b[i] = 0;
ntt.DFT(C,1); ntt.DFT(b,1);
for(int i = 0;i<ntt.N;i++)
b[i] = ((2LL - C[i] * b[i] % P) + P) % P * b[i] % P;
ntt.DFT(b,-1);
for(int i = deg;i<ntt.N;i++) b[i] = 0;
}
void Direv(int n,ll* A,ll* B,int flag){
if(flag == 1){
for(int i = 1;i<n;i++) B[i-1] = A[i] * i % mod;
B[n - 1] = 0;
}
else{
for(int i = 1;i<n;i++) B[i] = A[i-1] * inv[i] % mod;
B[0] = 0;
}
}
void get_log(int n,ll* a,ll* b){
Direv(n,a,A,1);
poly_inv(n,a,B);
ntt.DFT(A,1); ntt.DFT(B,1);
for(int i = 0;i<ntt.N;i++) A[i] = A[i] * B[i] % mod;
ntt.DFT(A,-1);
Direv(n,A,b,-1);
for(int i = 0;i<ntt.N;i++) A[i] = B[i] = 0;
for(int i = n;i<ntt.N;i++) b[i] = 0;
}
int main(){
init();
int T;
scanf("%d",&T);
for(int cas = 1;cas<=T;cas++){
int n,m;
scanf("%d%d",&n,&m);
for(int i = 0;i <= n + 100;i++){
ll c2 = (ll)i * (i-1) / 2;
f[i] = qpow(m+1,c2);
// f[i] = f[i] * Inv(fac[i]) % mod;
// if(i<10) cout<
f[i] = f[i] * finv[i] % mod;
}
// puts("");
get_log(n+10,f,G);
ll ans = G[n] * fac[n] % mod;
ll ret = 0;
if(n >= 2) ret = qpow(n,n-2) * qpow(m,n-1) % mod;
ans = (ans + mod - ret) % mod;
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}