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
*/