【loj6538】烷基計數 加強版 加強版 Burnside引理+多項式牛頓迭代

別問我爲啥突然刷了道OI題,也別問我爲啥花括號不換行了...

題目描述

求含 $n$ 個碳原子的本質不同的烷基數目模 $998244353$ 的結果。$1\le n\le 10^5$ 。


題解

Burnside引理+多項式牛頓迭代

不考慮同構的話,很容易想到dp方程 $\begin{cases}f_0=1\\f_i=\sum\limits_{j+k+l+1=i}f_jf_kf_l\end{cases}$ 。

考慮同構,可以通過容斥原理,大力討論一下容斥係數。一個更簡單的方法是考慮Burnside引理,即:等價類的數目等於每個置換下不動點數目的平均值。

  • 對於置換 $(1,2,3)$ ,所有組合都是不動點;
  • 對於置換 $(1,3,2)$ 、$(2,1,3)$ 和 $(3,2,1)$ ,“$2+1$” 的組合是不動點;
  • 對於置換 $(2,3,1)$ 和 $(3,1,2)$ ,只有 “$3$” 的組合是不動點。

於是新的dp方程爲 $\begin{cases}f_0=1\\f_i=\frac{\sum\limits_{j+k+l+1=i}f_jf_kf_l+\sum\limits_{2j+k+1=i}3f_jf_k+\sum\limits_{3j+1=i}2f_j}6\end{cases}$ 。

$n$ 這麼大肯定不能直接dp,考慮多項式解法,則dp方程的多項式形式爲 $F(x)=x\cdot\frac{F^3(x)+3F(x)F(x^2)+2F(x^3)}6+1$ 。

由於出現了三次方和 $F(x^2)$ 、$F(x^3)$ 項,因此這個方程難以直接解出。

考慮牛頓迭代,則當我們已知 $F(x)\mod x^n$ 時,$F(x^2)\mod x^{2n}$ 和 $F(x^3) \text{mod} x^{2n}$ 就已經是已知量,在迭代時可以當作常量處理。

記 $S(x)=F(x^2)$ ,$C(x)=F(x^3)$ ,則我們要迭代的方程就是 $G(T(x))=x\cdot\frac{T^3(x)+3S(x)T(x)+2C(x)}6-T(x)+1$ 的零點 $G(F(x))=0$ 。

又因爲 $G'(T(x))=x\cdot\frac{3T^2(x)+3S(x)}6-1$ ,代入牛頓迭代公式 $F(x)=F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))}$ 中可得 $F(x)=F_0(x)-\frac{x(F_0^3(x)+3S(x)F_0(x)+2C(x))-6F_0(x)+6}{3F_0^2(x)+3S(x)-6}$ ,其中 $F_0(x)$ 是上次迭代所得的多項式。

最後的答案就是 $F(x)[n]$ 。

時間複雜度 $O(n\log n)$ 。

#include <cstdio>
#include <algorithm>
#define N 262155
#define mod 998244353
using namespace std;
typedef long long ll;
ll F[N];
inline ll qpow(ll x , ll y) {
    ll ans = 1;
    while(y) {
        if(y & 1) ans = ans * x % mod;
        x = x * x % mod , y >>= 1;
    }
    return ans;
}
inline void ntt(ll *A , int n , ll flag) {
    int i , j , k;
    for(k = i = 0 ; i < n ; i ++ ) {
        if(i < k) swap(A[i] , A[k]);
        for(j = (n >> 1) ; (k ^= j) < j ; j >>= 1);
    }
    for(k = 2 ; k <= n ; k <<= 1) {
        ll wn = qpow(3 , (mod - 1) / k * flag);
        for(i = 0 ; i < n ; i += k) {
            ll w = 1 , t;
            for(j = i ; j < i + (k >> 1) ; j ++ , w = w * wn % mod)
                t = w * A[j + (k >> 1)] % mod , A[j + (k >> 1)] = (A[j] - t + mod) % mod , A[j] = (A[j] + t) % mod;
        }
    }
    if(flag == mod - 2) {
        ll t = qpow(n , flag);
        for(i = 0 ; i < n ; i ++ ) A[i] = A[i] * t % mod;
    }
}
inline void inv(ll *A , ll *B , int n) {
    static ll T[N];
    int i , j;
    for(i = 0 ; i < (n << 1) ; i ++ ) B[i] = 0;
    B[0] = qpow(A[0] , mod - 2);
    for(i = 2 ; i <= n ; i <<= 1) {
        for(j = 0 ; j < i ; j ++ ) T[j] = A[j] , T[j + i] = 0;
        ntt(T , i << 1 , 1) , ntt(B , i << 1 , 1);
        for(j = 0 ; j < (i << 1) ; j ++ ) B[j] = B[j] * (2 - T[j] * B[j] % mod + mod) % mod;
        ntt(B , i << 1 , mod - 2);
        for(j = i ; j < (i << 1) ; j ++ ) B[j] = 0;
    }
}
inline void solve(int n) {
    static ll G[N] , H[N] , S[N] , C[N] , T[N];
    int i , j;
    F[0] = 1;
    for(i = 2 ; i <= n ; i <<= 1) {
        for(j = 0 ; j < i ; j ++ ) T[j] = F[j] , S[j] = C[j] = T[j + i] = S[j + i] = C[j + i] = 0;
        for(j = 0 ; j < i ; j += 2) S[j] = F[j / 2];
        for(j = 0 ; j < i ; j += 3) C[j] = F[j / 3];
        ntt(T , i << 1 , 1) , ntt(S , i << 1 , 1);
        for(j = 0 ; j < (i << 1) ; j ++ ) G[j] = T[j] * (T[j] * T[j] % mod + 3 * S[j]) % mod , H[j] = 3 * (T[j] * T[j] + S[j]) % mod;
        ntt(G , i << 1 , mod - 2) , ntt(H , i << 1 , mod - 2);
        for(j = i ; j < (i << 1) ; j ++ ) G[j] = H[j] = 0;
        for(j = i - 1 ; j ; j -- ) G[j] = ((G[j - 1] + 2 * C[j - 1] - 6 * F[j]) % mod + mod) % mod , H[j] = H[j - 1];
        G[0] = 0 , H[0] = mod - 6;
        ntt(G , i << 1 , 1) , inv(H , T , i) , ntt(T , i << 1 , 1);
        for(j = 0 ; j < (i << 1) ; j ++ ) G[j] = G[j] * T[j] % mod;
        ntt(G , i << 1 , mod - 2);
        for(j = 0 ; j < i ; j ++ ) F[j] = (F[j] - G[j] + mod) % mod;
    }
}
int main() {
    int n , len = 1;
    scanf("%d" , &n);
    while(len <= n) len <<= 1;
    solve(len);
    printf("%lld\n" , F[n]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章