1. 程式人生 > 其它 >21.7.10 t2

21.7.10 t2

tag:fwt,多項式快速冪


這是一道看上去很難的題。

但是你仔細分析一下就會發現,\(c_i\) 沒用。。

因為有個 \(\mu\) 的存在,所以只需 \(2^k\) 列舉所有情況計算。

然後問題在於如何快速計算。


對於 \(f^a(x)\),由於 \(x\) 一定可以表示為 \(p_1\times\cdots\times p_k\),所以實際上相當於是

\[f^a(x)=\sum_{\bigcap S_i=[1,k],\forall i!=j,S_i\cup S_j=\varnothing}f(\prod S_i) \]

用人話來講就是,把 \(k\) 個質數分為 \(a\) 組(可以有空組),每組的貢獻為 \(f(\prod p)\)

,然後一個分組方案的貢獻就是所有組貢獻的乘積。


對於 \(\phi^a(x)\),由於 \(\phi\) 是積性函式,所以不管怎麼分組貢獻都是 \(\phi(x)\),而分組方案為 \(a^k\),所以 \(\phi^a(x)=a^k\phi(x)\)


對於 \(f^b(x)\) 就沒有比較直接的方法了。

這個時候我們看到資料範圍有一個 \(k\le16,b\le8\),於是想到暴力 dp。

\(f[i][S]\) 表示前 \(i\) 組分了 \(S\),轉移時列舉子集,複雜度 \(O(b3^k)\)

然後我們又看到了資料範圍有一個 \(k\le18,b\le8\),於是想到了不交併卷積

,複雜度 \(O(bk^22^k)\)

然後我們發現不交併中間的步驟可以表示為一個長度為 \(k\) 的多項式 \(F_i=f[i][S]\)\(b\) 次方,於是我們想到了執行 \(2^k\) 次多項式快速冪。

然後我們發現 fwt 後 \(f[0][S]=1\),於是想到了 \(\ln\) 然後 \(\exp\)

複雜度 \(O(2^kk^2)\)


#include<bits/stdc++.h>
using namespace std;

template<typename T>
inline void Read(T &n){
	char ch; bool flag=false;
	while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
	for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
	if(flag)n=-n;
}

typedef long long ll;
enum{
    MOD = 998244353
};

inline int ksm(int base, ll k=MOD-2){
    int res=1;
    while(k){
        if(k&1)
            res = 1ll*res*base%MOD;
        base = 1ll*base*base%MOD;
        k >>= 1;
    }
    return res;
}

inline int inc(int a, int b){
    a += b;
    if(a>=MOD) a -= MOD;
    return a;
}

inline int dec(int a, int b){
    a -= b;
    if(a<0) a += MOD;
    return a;
}

inline void iinc(int &a, int b){a = inc(a,b);}
inline void ddec(int &a, int b){a = dec(a,b);}
inline void upd(int &a, long long b){a = (a+b)%MOD;}

int p[20], c[20];
int k, ans;
ll a, b;

namespace Case2{
    inline void fwt(int *f, int n, int flag){
        for(int len=2; len<=n; len<<=1)
            for(int l=0; l<n; l+=len)
                for(int i=l; i<l+len/2; i++)
                    if(flag==1) iinc(f[i+len/2],f[i]);
                    else ddec(f[i+len/2],f[i]); 
    }

    int f[19][1<<18], cnt[1<<18], tmp[19][1<<18];
    int F[20], G[20], inv[20];
    inline void solve(){
        int tp=1<<k;
        for(int i=1; i<=k; i++) inv[i] = ksm(i);
        for(int s=0; s<tp; s++){
            int mul=1;
            for(int i=0; i<k; i++) if(s>>i&1) mul = 1ll*mul*p[i]%MOD, cnt[s]++;
            f[cnt[s]][s] = 1;
            upd(f[cnt[s]][s],1ll*mul*mul);
            ddec(f[cnt[s]][s],mul);
        }
        for(int i=0; i<=k; i++) fwt(f[i],tp,1);
        for(int s=0; s<tp; s++){
            for(int i=0; i<=k; i++) F[i] = f[i][s];
            for(int i=1; i<=k; i++){
                G[i] = 0;
                for(int j=1; j<i; j++) ddec(G[i],1ll*j*G[j]%MOD*F[i-j]%MOD);
                G[i] = (F[i]+1ll*inv[i]*G[i])%MOD;
            }
            G[0] = 0;
            for(int i=1; i<=k; i++) G[i] = b*G[i]%MOD;
            for(int i=1; i<=k; i++){
                int tmp=0;
                for(int j=1; j<=i; j++) upd(tmp,1ll*j*G[j]%MOD*F[i-j]);
                F[i] = 1ll*tmp*inv[i]%MOD;
            }
            F[0] = 1;
            for(int i=0; i<=k; i++) f[i][s] = F[i];
        }
        for(int i=0; i<=k; i++) fwt(f[i],tp,-1);

        for(int s=0; s<tp; s++){
            int phi=1;
            for(int i=0; i<k; i++) if(s>>i&1) phi = 1ll*phi*(p[i]-1)%MOD;
            phi = 1ll*phi*ksm(a,cnt[s])%MOD;
            if(cnt[s]&1) ddec(ans,1ll*phi*f[cnt[s]][s]%MOD);
            else upd(ans,1ll*phi*f[cnt[s]][s]);
            // for(int i=0; i<k; i++) putchar(s>>i&1^48); printf(" %d\n",phi);
        }
        cout<<ans<<'\n';
    }
}

int main(){
    // freopen("dirichlet3.in","r",stdin);
    Read(a); Read(b); Read(k); a %= MOD; b %= MOD;
    for(int i=0; i<k; i++) Read(p[i]), Read(c[i]);
    // if(a<=8 and b<=8) return 
    Case2::solve();
    return 0;
}

/*
1 2
3
2 1 3 2 5 4
*/