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
*/
posted @ 2021-07-10 15:36  oisdoaiu  阅读(32)  评论(0编辑  收藏  举报