单位根反演是用单位根来表示整除关系的东西。
定义式
如果 \(k \mid n\),那么 \(\omega_k^{n}=(\omega_k^{k})^{\frac nk}=1\),从而 \(\omega_{k}^{in}=1\),于是右边和式的值等于 \(k\),乘上 \(\dfrac{1}{k}\) 后等于 \(1\)。
如果 \(k \nmid n\),那么 \(\omega_{k}^n \neq 1\),此时使用等比数列求和公式求得右边等于 \(\dfrac{\omega_{k}^{nk}-\omega_k^{0}}{\omega_{k}^n-1} = 0\)。
这样和式里面需要枚举整除关系的时候我们就可以用这个来代替,方便简化式子。
例题 Luogu P5591 小猪佩奇学数学
题意
给定 \(n,p,k\),求
\(1 \leq n,p <998244353,k \in \{2^{w}|0 \leq w \leq 20\}\)
题解
注意到这东西不能整除分块,于是我们先对那个下取整开刀,变成 \(\dfrac{i - i \bmod k}{k}\),然后提出 \(\dfrac{1}{k}\),于是原式等于
不管外面那个 \(\dfrac{1}{k}\),先看左边的那个和式,吸收公式
考虑右边的 \(\sum \limits_{i=0}^n \dbinom{n}{i} p^i \times i \bmod k\)。我们并没有什么办法来处理 \(i \bmod k\),于是枚举它,
接上单位根反演
第二个求和号很像二项式,于是把它往里面放
里面的一坨只和 \(j\) 一个变量相关,考虑交换求和号
第二个求和号是一个差比数列求和,求和方法和等比数列类似,考虑多算一项,和当前项做差后比较系数。形如
的数列,记其和为 \(S\),那么
移项,得
如果 \(a=1\),那么 \(S=\dfrac{n(n-1)}{2}\)。
再来看上面的式子
预处理单位根,枚举 \(j\) 的时候用快速幂算 \((1+p\cdot \omega_k^j)^n\) 和后面的差比数列和即可,只有一个 \(\log\)。
# include <bits/stdc++.h>
const int N=1048580,INF=0x3f3f3f3f,MOD=998244353;
int w[N];
int n,p,k;
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
inline int qpow(int d,int p){
int ans=1;
for(;p;p>>=1,d=1ll*d*d%MOD) (p&1)&&(ans=1ll*ans*d%MOD);
return ans;
}
inline int add(int x,int y){
return (x+y>=MOD)?(x+y-MOD):(x+y);
}
inline int mi(int x,int y){
return (x-y<0)?(x-y+MOD):(x-y);
}
inline int S(int r,int n){
if(r==1) return 1ll*n*(n-1)/2%MOD;
return mi(1ll*n*qpow(r,n)%MOD*qpow(r-1,MOD-2)%MOD,1ll*mi(qpow(r,n+1),r)*qpow(qpow(r-1,2),MOD-2)%MOD);
}
int main(void){
n=read(),p=read(),k=read();
int ans=1ll*n*p%MOD*qpow(p+1,n-1)%MOD,sum=0;
w[0]=1,w[1]=qpow(3,(MOD-1)/k);
for(int i=2;i<=k;++i) w[i]=1ll*w[i-1]*w[1]%MOD;
for(int i=0;i<k;++i) sum=add(sum,1ll*qpow(add(1ll*w[i]*p%MOD,1),n)*S(w[k-i],k)%MOD);
ans=mi(ans,1ll*sum*qpow(k,MOD-2)%MOD);
printf("%lld",1ll*ans*qpow(k,MOD-2)%MOD);
return 0;
}