单位根反演小记
command_block · · 个人记录
首先您需要知道单位根是个什么东西 : 傅里叶变换(FFT)学习笔记
先抛一个等式:
①
证明 :
-
a≠0\pmod n 使用等比数列求和。
原式
=\dfrac{1}{n}\sum\limits_{k=0}^{n-1}w_n^{ak}=\dfrac{1}{n}*\dfrac{w_n^{an}-1}{w_n^a-1} 又因为
w_n^{a}-1=w_n^0-1=0 ,且w_n^a-1≠0 所以式子的值为0 。 -
a=0\pmod n 等比数列求和在公比为
1 时需要特判。原式
=\dfrac{1}{n}\sum\limits_{k=0}^{n-1}w_n^{ak\bmod n}=\dfrac{1}{n}\sum\limits_{i=0}^{n-1}w_n^{0}=1
可以导出下式
- ②
[a=b\pmod n]=[a-b=0\pmod n]=\dfrac{1}{n}\sum\limits_{i=0}^{n-1}w_n^{(a-b)k}=\dfrac{1}{n}\sum\limits_{i=0}^{n-1}w_n^{ak}w_n^{-bk}
应用①
- 和
\mod\ 有关的一系列东西。
例题 : Loj#6485. LJJ 学二项式定理
求
通过上面的结论可知
后面的一部分就能够使用二项式定理了 :
原式
众所周知
剩下的就一个快速幂了。
#include<algorithm>
#include<cstdio>
#define mod 998244353
#define ll long long
using namespace std;
ll powM(ll a,int t=mod-2)
{
ll ans=1;
while(t){
if (t&1)ans=ans*a%mod;
a=a*a%mod;
t>>=1;
}return ans;
}
const ll w[4]={1,911660635,998244352,86583718};
int a[5];
ll n,s,inv4=powM(4);
void solve()
{
scanf("%lld%lld%d%d%d%d",&n,&s,a,a+1,a+2,a+3);
n%=(mod-1);
ll ans=0,sum;
for (int i=0;i<4;i++){
sum=0;
for (int j=0;j<4;j++)
sum=(sum+w[((4-i)*j)&3]*powM(s*w[j]%mod+1,n))%mod;
ans=(ans+sum*a[i])%mod;
}printf("%lld\n",ans*inv4%mod);
}
int main()
{
int T;scanf("%d",&T);
while(T--)solve();
return 0;
}
P5591 小猪佩奇学数学
给定
首先这个整除一看就不可做的样子……
抖个机灵 :
那么原式
前面的
- 左半边的部分
最右边的
结论:
原式
- 右半边的部分
前面的
(到这里已经得到一个
这时后面的
设
S(n,k)=\sum\limits_{i=0}^{n-1}ik^i kS(n,k)-S(n,k)=\sum\limits_{i=0}^{n-1}ik^{i+1}-\sum\limits_{i=0}^{n-1}ik^i =\sum\limits_{i=1}^{n}(i-1)k^{i}-\sum\limits_{i=0}^{n-1}ik^i =-\sum\limits_{i=1}^{n-1}k^{i}+(n-1)k^{n} =\dfrac{1-k^{n+1}}{k-1}+1+(n-1)k^{n} 综上可解得
S(n,k)=\dfrac{\dfrac{1-k^{n}}{k-1}+(n-1)k^{n}+1}{k-1} 注意到带入的
k 总是n 次单位根,那么k^n=1 。S(n,k)=\dfrac{n}{k-1} 注意上述推导要求
k≠1 ,如果k=1 可得S(n,1)=\sum\limits_{i=0}^{n}i=\dfrac{(n-1)n}{2} 任意
S 函数都可以O(logn) 内计算出来。
原式
这就是一个
#include<algorithm>
#include<cstdio>
#define mod 998244353
#define ll long long
using namespace std;
ll powM(ll a,int t=mod-2)
{
ll ans=1;
while(t){
if (t&1)ans=ans*a%mod;
a=a*a%mod;
t>>=1;
}return ans;
}
int n,p,k;
ll w[1100000];
inline ll S(ll n,ll k)
{
if (k==1)return ((n-1)*n>>1)%mod;
return n*powM(k-1)%mod;
}
int main()
{
scanf("%d%d%d",&n,&p,&k);
ll buf=powM(3,(mod-1)/k),ans=0;
w[0]=1;
for (int i=1;i<=k;i++)
w[i]=w[i-1]*buf%mod;
for (int i=0;i<k;i++)
ans=(ans+powM((p*w[i]+1)%mod,n)*S(k,w[k-i]))%mod;
ans=ans*powM(k)%mod;
ans=(1ll*n*p%mod*powM(p+1,n-1)-ans+mod)%mod;
printf("%lld",ans*powM(k)%mod);
return 0;
}
附加练习:
- [数学记录]Uoj#450. 【集训队作业2018】复读机
应用②
-
-
某些需要用到单位根的题目里面可以用来反演/构造。
-
计算循环卷积
循环卷积即形为 :
把独立的部分组合起来
设
那么得到
变为了点积。
再定义
那么有
其实就是FFT的算法流程,平时我们使用的FFT就是在计算长度为
现在我们要解决
首先,一个比较憨逼的想法是,先计算双倍长度的普通加法卷积,然后再把该循环的部分手动添加。
这种做法把卷积当做黑盒使用,性质很不优美。
观察任意长度
考虑使用
-
式子1 :
\dfrac{k^2+i^2-(k-i)^2}{2}=ik\quad ( 除2 可能会产生一些不愉快 ) -
式子2 :
\dbinom{i+j}{2}-\dbinom{i}{2}-\dbinom{j}{2}=\dfrac{i+j(i+j-1)-i(i-1)-j(j-1)}{2}=ij
这里使用式子1,能得到:
将
这似乎称作 Bluestein 算法。
-
例题 : POJ2821 TN's Kingdom III - Assassination
题意 : 有三个序列
A,B,C ,已知A*B=C (循环卷积),给出B,C 求A .
循环卷积的“次数”是封闭的,所以我们只需要
循环卷积的求逆就是直接把变换而得的“点值”置为逆元(类似FWT位运算卷积的求逆)
-
进阶题 : P5293 [HNOI2019]白兔之舞
[数学记录]P5293 [HNOI2019]白兔之舞