【学习笔记】单位根反演
##### 基本柿子
$$
\frac{1}{n} \sum_{i=0}^{n-1} w_n^{ki} = [n|k]
$$
证明如下:
1.当$n|k$时,设$k=np$。
$\frac{1}{n} \sum_{i=0}^{n-1} w_{n}^{npi} = \frac{1}{n} \sum_{i=0}^{n-1} w_{n}^{0} = 1$
2.当$n \nmid k$时
$\frac{1}{n} \frac{w_n^{nk}-w_{n}^0}{w_n^k-1}=0$
##### 例题
BZOJ PYXFIB
$\sum_{i=0}^n \binom{n}{i} F_i [i\%d==0]$
$ \frac{1}{d} \sum_{i=0}^n \binom{n}{i} F_i \sum_{j=0}^{d-1} w_d^{ij}$
显然$F$要用矩乘加速
然后把$w_d^j$直接扔进去好了x
最后就是$\frac{1}{d}(w_n^j F + I)^n$
代码
//Love and Freedom. #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long #define inf 20021225 using namespace std; ll read() { ll s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct mat { int a[2][2]; mat(){} mat(int lu,int ru,int ld,int rd){a[0][0]=lu,a[0][1]=ru,a[1][0]=ld,a[1][1]=rd;} void clear(){memset(a,0,sizeof(a));} void det(){clear(); a[0][0]=a[1][1]=1;} }; int G,mdn; void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;} mat operator+(mat a,mat b) { mat tmp; for(int i=0;i<2;i++) for(int j=0;j<2;j++) tmp.a[i][j]=(a.a[i][j]+b.a[i][j])%mdn; return tmp; } mat operator*(mat a,mat b) { mat tmp; tmp.clear(); for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) upd(tmp.a[i][j],1ll*a.a[i][k]*b.a[k][j]%mdn); return tmp; } int fac[211],tot; int ksm(int bs,int mi) { int ans=1; while(mi) { if(mi&1) ans=1ll*ans*bs%mdn; bs=1ll*bs*bs%mdn; mi>>=1; } return ans; } void getG() { int top=mdn-1; tot=0; for(int i=1;i<=sqrt(top);i++) if(top%i==0) fac[++tot]=i,fac[++tot]=top/i; for(G=2;G<=100;G++) { int i; for(i=1;i<=tot;i++) if(fac[i]!=top&&ksm(G,fac[i])==1) break; if(i>tot) return; } } mat mksm(mat bs,ll mi) { mat ans; ans.det(); while(mi) { if(mi&1) ans=ans*bs; bs=bs*bs; mi>>=1; } return ans; } int main() { int T=read(); while(T--) { ll n=read(),k=read(); mdn=read(); getG(); int Wn=ksm(G,(mdn-1)/k); int ans=0; for(int i=0;i<k;i++) { int w=ksm(Wn,i); mat cur=mksm(mat(w+1,w,w,1),n); int val=cur.a[0][0]; upd(ans,val); } printf("%d\n",1ll*ans*ksm(k,mdn-2)%mdn); } return 0; }
LOJ6485. LJJ 学二项式定理
$\frac{1}{4}\sum_{i=0}^n \sum_{j=0}^3 a_j s^i \binom{n}{i} [i\%4==j]$
$\frac{1}{4}\sum_{i=0}^n\sum_{j=0}^3 a_j s^i\binom{n}{i} [(i-j)\%4==0]$
$\frac{1}{4}\sum_{i=0}^n\sum_{j=0}^3 a_j s^i\binom{n}{i} \sum_{k=0}^3 w_4^{(i-j)k}$
$\frac{1}{4}\sum_{j=0}^3 a_j \sum_{i=0}^n s^i \binom{n}{i} \sum_{k=0}^3 w_{4}^{(i-j)k}$
$\frac{1}{4}\sum_{j=0}^3 \sum_{k=0}^3 a_j w^{-jk}(sw_{4}^k+1)^n$
然后就做完惹
//Love and Freedom. #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long #define inf 20021225 #define mdn 998244353 #define G 3 #define inv4 748683265 using namespace std; ll read() { ll s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int ksm(int bs,ll mi) { int ans=1; while(mi) { if(mi&1) ans=1ll*ans*bs%mdn; bs=1ll*bs*bs%mdn; mi>>=1; } return ans; } int a[4]; int main() { int Wn=ksm(G,(mdn-1)/4); int T=read(); while(T--) { ll n=read(); int s=read(); int ans=0; for(int i=0;i<4;i++) { int w=Wn,cur=0; a[i]=read(); for(int j=0,k=1;j<4;j++,k=1ll*k*w%mdn) (cur+=1ll*ksm(1ll*s*k%mdn+1,n)*ksm(k,4-i)%mdn*inv4%mdn)%=mdn; (ans+=1ll*cur*a[i]%mdn)%=mdn; } printf("%d\n",ans); } return 0; }