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次方。

posted @ 2020-12-07 22:06  Yuhuger  阅读(152)  评论(0编辑  收藏  举报