Dirichelet卷积的k次方根
迪利克雷卷积的k次方可以直接快速幂,怎么求k次方根呢?(模意义下)
裸题https://codeforces.com/gym/102471/problem/C
听到了两种做法,一种是一位一位爆推,dp[i][j]记为有顺序的乘积为j,i个不为1的方案数。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=998244353;
const int N=100010;
int fp(int x,int y){
int ret=1;
for (; y; y>>=1,x=(ll)x*x%M)
if (y&1) ret=(ll)ret*x%M;
return ret;
}
int n,k;
int f[N],fac[N],invfac[N],ck[N],dp[18][N],c[18],g[N];
int C(int x,int y){
return (ll)fac[x]*invfac[x-y]%M*invfac[y]%M;
}
int main(){
scanf("%d%d",&n,&k);
for (int i=1; i<=n; ++i) scanf("%d",&g[i]);
fac[0]=1;
for (int i=1; i<18; ++i) fac[i]=(ll)fac[i-1]*i%M;
invfac[17]=fp(fac[17],M-2);
for (int i=16; i>=0; --i) invfac[i]=(ll)invfac[i+1]*(i+1)%M;
ck[0]=1;
for (int i=1; i<18; ++i)
ck[i]=(ll)ck[i-1]*(k-i+1)%M;
for (int i=1; i<18; ++i)
ck[i]=(ll)ck[i]*invfac[i]%M;
dp[0][1]=1;
f[1]=1;
for (int i=2; i<=n; ++i){
// cerr<<"init"<<i<<endl;
int sigma=0;
for (int j=0; j<min(k+1,18); j++)
(sigma+=(ll)dp[j][i]*ck[j]%M)%=M;
f[i]=1ll*(g[i]-sigma+M)%M*fp(k,M-2)%M;
for (int d=min(17,k); d>=0; --d){
for (int k=1; k<=n/i; ++k)
if (dp[d][k]){
for (ll mi=1,now=i,fc=f[i]; k*now<=n&&d+mi<=::k; ++mi,now*=i,fc=(ll)f[i]*fc%M){
(dp[d+mi][k*now]+=(ll)dp[d][k]*C(d+mi,mi)%M*fc%M)%=M;
}
}
}
}
for (int i=1; i<=n; ++i) cout<<f[i]<<" ";
}
还有一种利用
\[f^{mod}={1,0,0,0,0,0,0,0,...}
\]
转化为$$g^{1/k}$$其中\(1/k\)为\(k\)在\(mod\)下的逆元,转化为k次方。