反演曾經一直是我不敢搞的一個大坑……php
又從新學習了一下反演,而且作了一些習題……html
大概基礎什麼的……我就介紹一點經常使用的node
正經反演的式子有這樣兩種數組
$$f(n)=\sum _{d|n}F(d) \mu(\frac{n}{d})$$數據結構
以及ide
$$f(n)=\sum _{n|d} F(d)\mu(\frac{d}{n})$$函數
不少題都要用關於狄利克雷卷積的一個式子學習
$\mu \otimes 1 = e$,也即$\sum _{d|n} \mu(d)=[n==1]$ui
證實的話……咱們能夠用二項式定理來證,網上有不少證實,我就不證了spa
基本上,全部題目的轉換都會利用這個式子
咱們通常會搞一個$[???==1]$,而後套用上面那個關於$\sum\mu$的式子
…………板子題就不放了,每道題都挺水的
通常就是枚舉$gcd$,而後判$[gcd==d]$的時候才累加,
而後除掉一個$d$變成$[gcd==1]$,而後套式子.
寫一些比較有意思的題目好了。
這題反演的式子仍是能夠推的……甚至說比較板子
比較好的思想是咱們把詢問離線按a排序,下標i按照F值排序
而後用樹狀數組維護答案而且更新
數學與數據結構的優秀結合……
貼代碼:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef long long LL; 6 const int N=100010; 7 const unsigned int inf=0x7fffffff; 8 bool vis[N+10];int tot; 9 unsigned int prime[N],sumd[N+10]; 10 unsigned int tmp1[N+10],tmp2[N+10],mu[N+10],p[N+10]; 11 struct node{unsigned int ans,id,n,m,a;}q[20010]; 12 inline bool mt1(const node &a,const node &b){return a.a<b.a;} 13 inline bool mt2(const int &a,const int &b){return sumd[a]<sumd[b];} 14 inline bool mt3(const node &a,const node &b){return a.id<b.id;} 15 inline void intn() 16 { 17 int k;mu[1]=sumd[1]=1; 18 for(int i=2;i<=N;i++) 19 { 20 if(!vis[i]) 21 { 22 prime[++tot]=tmp2[i]=i; 23 sumd[i]=tmp1[i]=i+1,mu[i]=-1; 24 } 25 for(int j=1;j<=tot&&(k=i*prime[j])<=N;j++) 26 { 27 vis[k]=1; 28 if(i%prime[j]==0) 29 { 30 tmp2[k]=tmp2[i]*prime[j],tmp1[k]=tmp1[i]+tmp2[k]; 31 sumd[k]=sumd[i]/tmp1[i]*tmp1[k]; 32 mu[k]=0;break; 33 } 34 sumd[k]=sumd[i]*sumd[prime[j]]; 35 tmp1[k]=1+prime[j];tmp2[k]=prime[j]; 36 mu[k]=-mu[i]; 37 } 38 } 39 for(int i=1;i<=N;i++)p[i]=i; 40 sort(p+1,p+N+1,mt2); 41 } 42 unsigned int bit[N+10]; 43 inline int lowbit(int a){return a&-a;} 44 inline void add(int pos,unsigned int val){while(pos<=N)bit[pos]+=val,pos+=lowbit(pos);} 45 inline unsigned int query(int pos) 46 {unsigned int ret=0;while(pos)ret+=bit[pos],pos-=lowbit(pos);return ret;} 47 inline unsigned int calc(unsigned int n,unsigned int m) 48 { 49 if(n>m)swap(n,m); 50 unsigned int ret=0,last; 51 for(unsigned int i=1;i<=n;i=last+1) 52 { 53 last=min(n/(n/i),m/(m/i)); 54 ret+=(n/i)*(m/i)*(query(last)-query(i-1)); 55 } 56 return ret; 57 } 58 int main() 59 { 60 intn();int top=1,t;scanf("%d",&t); 61 for(int i=1;i<=t;i++) 62 q[i].id=i,scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a); 63 sort(q+1,q+t+1,mt1); 64 for(int i=1;i<=t;i++) 65 { 66 while(top<=N&&sumd[p[top]]<=q[i].a) 67 { 68 for(int k=1;k*p[top]<=N;k++) 69 add(k*p[top],mu[k]*sumd[p[top]]); 70 top++; 71 } 72 q[i].ans=calc(q[i].n,q[i].m); 73 } 74 sort(q+1,q+t+1,mt3); 75 for(int i=1;i<=t;i++) 76 printf("%d\n",q[i].ans&inf); 77 }
咱們知道,題給的$f$是一個積性函數
而後咱們枚舉一下$gcd$,則有
$ans=\sum _{i=1} ^{n} \sum _{j=1} ^{m} f((i,j))$
$=\sum _{d=1} ^{n} f(d)\sum _{i=1} ^{n} \sum _{j=1} ^{m} [(i,j)==d]$
$=\sum _{d=1} ^{n} f(d)\sum _{i=1} ^{\left \lfloor \frac {n}{d} \right \rfloor } \sum _{j=1} ^{\left \lfloor \frac {m}{d} \right \rfloor } [(i,j)==1]$
$=\sum _{d=1} ^{n} f(d) \sum _{g=1} ^{ \left \lfloor \frac {n}{d} \right \rfloor } \mu(g) \left \lfloor \frac {n}{dg} \right \rfloor \left \lfloor \frac {m}{dg} \right \rfloor$
而後咱們轉而枚舉$T=dg$
則原式有
$\sum _{T=1} ^{n} \left \lfloor \frac {n}{T} \right \rfloor \left \lfloor \frac {m}{T} \right \rfloor \sum _{d|T}f(d)\mu(\frac{T}{d})$
那麼咱們若是能處理內層函數$\sum _{d|T}f(d)\mu(\frac{T}{d})$的前綴和,就能$O(\sqrt{n})$解決這題了
而後……咱們來考察一下內層函數怎麼求
咱們固然能夠$O(nlogn)$暴力預處理
可是不知道能不能過……因爲$f$和$\mu$都是積性函數,所以它們的狄利克雷卷積也是積性函數
咱們設$g(T)=\sum _{d|T}f(d)\mu(\frac{T}{d})$
咱們設$T=\prod p_{i} ^ { a_{i} }$
那麼$\frac{T}{d}$中每一個$p_{i}$都只有0個或者1個(多了$\mu$就是0了)
又因爲$f(d)$與最大的$a_{i}$有關,咱們考慮按照a_{i}討論
咱們設$a_{i}$最大的因子集合爲A,不最大的爲B
若是存在$i\neq j$,有$a_{i}\neq a_{j}$
無論A集合如何選擇,B集合的每一個因子都有選和不選2種選擇,從而使得$\mu$值一正一負,最後值爲0
即,存在$i\neq j,a_{i}\neq a_{j}$時,$g(T)=0$
反之,若是對於任意$i\neq j$,有$a_{i}=a_{j}$
那麼咱們全部的因子都屬於A集合,每一個因子都是有選和不選2種選擇,從而使得$\mu$值一正一負。
可是對於另一個f函數來講,若是$\mu$中出現了所有因子,那麼$f=a-1$,不然$f=a$
所以此時,$g(T)=-1*(-1)^{k}=(-1)^{k+1}$,k爲T的質因子個數
有了這些……咱們就能夠寫線性篩的式子了
線性篩挺簡單的……本身根據關係推一下就行
這題反演的式子好說,關鍵是構造函數線篩的分析。
這個分析很妙……結合了函數的特色進行了討論。
放個代碼:
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 #define N 10000010 5 #define K 10000000 6 #define LL long long 7 char B[1<<15],*S=B,*T=B; 8 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 9 inline int read() 10 { 11 int x=0;register char c=getc; 12 while(c<'0'||c>'9')c=getc; 13 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc; 14 return x; 15 } 16 int prime[K/10],tot,vis[N]; 17 int mincnt[N],g[N],ppow[N]; 18 inline void intn() 19 { 20 register int i,j,tmp; 21 for(i=2;i<=K;++i) 22 { 23 if(!vis[i])prime[++tot]=i,ppow[i]=mincnt[i]=g[i]=1; 24 for(j=1;j<=tot&&(tmp=i*prime[j])<=K;++j) 25 { 26 vis[tmp]=1; 27 if(i%prime[j]==0) 28 { 29 mincnt[tmp]=mincnt[i]+1,ppow[tmp]=ppow[i]; 30 if(1==ppow[tmp])g[tmp]=1; 31 else g[tmp]=(mincnt[ppow[tmp]]==mincnt[tmp])?-g[ppow[tmp]]:0; 32 break; 33 } 34 mincnt[tmp]=1,ppow[tmp]=i, 35 g[tmp]=(mincnt[i]==1)?-g[i]:0; 36 } 37 } 38 for(i=1;i<=K;++i)g[i]+=g[i-1]; 39 } 40 inline int min(int a,int b){return a<b?a:b;} 41 int main() 42 { 43 // freopen("Ark.in","r",stdin); 44 register int t,n,m,i,j; 45 t=read();LL ans=0;intn(); 46 while(t--) 47 { 48 n=read(),m=read(); 49 if(n>m)n^=m,m^=n,n^=m; 50 for(i=1,ans=0;i<=n;i=j+1) 51 j=min(n/(n/i),m/(m/i)),ans+=(n/i)*1ll*(m/i)*(g[j]-g[i-1]); 52 printf("%lld\n",ans); 53 } 54 }
一開始依然使用枚舉$gcd$的套路
過程不寫了,結果大概是:
$\prod_{d=1}^{n} fibo(d) ^ { \sum_{g=1} ^ { \left \lfloor \frac{n}{d}\right \rfloor } \mu(g) \left \lfloor \frac{n}{dg}\right \rfloor\left \lfloor \frac{m}{dg}\right \rfloor }$
而後咱們枚舉$T=dg$,有
$\prod_{T=1}^{n} ( \prod_{d|t} fibo(d) ^ { \mu( \frac{T}{d} ) } ) ^{\left \lfloor \frac{n}{dg}\right \rfloor \left \lfloor \frac{m}{dg}\right \rfloor} $
而後咱們設$g(T)=\prod_{d|t} fibo(d) ^ { \mu( \frac{T}{d} ) } $
只要預處理一下這個g函數的前綴積以及其逆元,咱們就能除法分塊求這個問題了。
而後呢……咱們能夠用組合數時候那種線性求逆元來搞,就是用費馬小定理求出最後一項逆元,而後在用乘除關係線性回推
可是直接求也不是很好求……咱們處理一下斐波那契數列的值及其逆元,而後用$nln_{n}$的枚舉倍數處理出來每一個$g(T)$
而後再用一遍線性求逆元……
這代碼只能說,短可是步驟不少……
這個線性求逆元的想法很好!
貼個代碼:
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 #define N 1000010 5 #define K 1000000 6 #define LL long long 7 #define mod 1000000007 8 char B[1<<15],*S=B,*T=B; 9 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 10 inline int read() 11 { 12 int x=0;register char c=getc; 13 while(c<'0'||c>'9')c=getc; 14 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc; 15 return x; 16 } 17 inline LL quick_mod(LL di,LL mi) 18 { 19 LL ans=1; 20 for(di%=mod;mi;mi>>=1,di=di*di%mod) 21 if(mi&1)ans=ans*di%mod; 22 return ans; 23 } 24 LL f[N],g[N],invf[N],invg[N],h[N],x[N],invx[N]; 25 int prime[N/10],tot,vis[N],mu[N]; 26 inline void intn() 27 { 28 register int i,j,tmp; 29 for(f[0]=0,f[1]=g[1]=1,i=2;i<=K;++i) 30 f[i]=(f[i-1]+f[i-2])%mod,g[i]=g[i-1]*f[i]%mod; 31 for(invg[K]=quick_mod(g[K],mod-2),i=K;i;--i) 32 h[i]=1,invg[i-1]=invg[i]*f[i]%mod,invf[i]=invg[i]*g[i-1]%mod; 33 for(mu[1]=1,i=2;i<=K;++i) 34 { 35 if(!vis[i])prime[++tot]=i,mu[i]=-1; 36 for(j=1;j<=tot&&(tmp=prime[j]*i)<=K;++j) 37 { 38 vis[tmp]=1; 39 if(i%prime[j]==0){mu[tmp]=0;break;} 40 mu[tmp]=-mu[i]; 41 } 42 } 43 for(i=2;i<=K;++i) 44 for(j=1;(tmp=i*j)<=K;++j) 45 if(mu[j]==1)h[tmp]=h[tmp]*f[i]%mod; 46 else if(mu[j]==-1)h[tmp]=h[tmp]*invf[i]%mod; 47 for(x[1]=h[1]=1,i=2;i<=K;++i)x[i]=x[i-1]*h[i]%mod; 48 invx[K]=quick_mod(x[K],mod-2); 49 for(i=K;i;--i)invx[i-1]=invx[i]*h[i]%mod; 50 } 51 inline int min(int a,int b){return a<b?a:b;} 52 int main() 53 { 54 register int i,j,t,n,m; 55 t=read();intn();LL ans,tmp; 56 while(t--) 57 { 58 n=read(),m=read(); 59 if(n>m)n^=m,m^=n,n^=m; 60 for(ans=i=1;i<=n;i=j+1) 61 { 62 j=min(n/(n/i),m/(m/i)), 63 ans=ans*quick_mod( x[j]*invx[i-1]%mod , (n/i)*1ll*(m/i)%(mod-1) )%mod; 64 } 65 printf("%lld\n",ans); 66 } 67 }
……讓人沒法形容的恐懼
我只能說恐懼……恐懼……
其實,反演的部分仍是能夠推式子的
可是最後的那一下……簡直算是神來之筆
誰會去猜想$\sum_{i=1}^{n} i^{k}$能夠寫成一個$k+1$次的多項式啊!
而後咱們拿到這個多項式以後才能繼續推式子……
或者……咱們能夠用更加使人懵逼的伯努利數
感受到了本身的愚蠢
附上蒟弱的代碼
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 #define LL long long 5 #define N 1010 6 #define D 110 7 #define mod 1000000007 8 char cB[1<<15],*S=cB,*T=cB; 9 #define getc (S==T&&(T=(S=cB)+fread(cB,1,1<<15,stdin),S==T)?0:*S++) 10 inline int read() 11 { 12 int x=0;register char c=getc; 13 while(c<'0'||c>'9')c=getc; 14 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc; 15 return x; 16 } 17 inline int quick_mod(int di,int mi) 18 { 19 if(mi<0)return quick_mod(quick_mod(di,mod-2),-mi); 20 int ret=1; 21 for(di%=mod;mi;mi>>=1,di=(LL)di*di%mod) 22 if(mi&1)ret=(LL)ret*di%mod; 23 return ret; 24 } 25 int d,w,p[N],A,a[N]; 26 int fac[D],inv[D],invf[D],B[D]; 27 #define C(i,j) ((LL)fac[i]*invf[j]%mod*invf[i-j]%mod) 28 int main() 29 { 30 // freopen("Ark.in","r",stdin); 31 register int i,j,k,ans,sum,tot=1; 32 d=read(),w=read(); 33 for(i=1;i<=w;++i) 34 p[i]=read(),a[i]=read(),tot=(LL)tot*quick_mod(p[i],a[i])%mod; 35 for(fac[0]=fac[1]=1,i=2;i<=d+2;++i)fac[i]=(LL)fac[i-1]*i%mod; 36 for(inv[0]=inv[1]=1,i=2;i<=d+2;++i)inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod; 37 for(invf[0]=invf[1]=1,i=2;i<=d+2;++i)invf[i]=(LL)invf[i-1]*inv[i]%mod; 38 for(B[0]=1,i=1;i<=d+2;++i) 39 { 40 for(B[i]=0,j=0;j<i;++j) 41 B[i]=(B[i]+(LL)C(i+1,j)*B[j]%mod)%mod; 42 B[i]=((LL)-B[i]*inv[i+1]%mod+mod)%mod; 43 } 44 for(ans=0,i=1;i<=d+1;++i) 45 { 46 A=((LL)( ((d+1-i)&1)?-1:1 )*C(d+1,i)*B[d+1-i]%mod*inv[d+1]%mod+mod)%mod; 47 if(A==0)continue; 48 // printf("%d\n",A); 49 for(sum=quick_mod(tot,i),j=1;j<=w;++j) 50 sum=(LL)sum*( mod+ 1ll- quick_mod(p[j],d-i) ) %mod; 51 // printf("sum=%d\n",sum); 52 ans=(ans+(LL)A*sum%mod)%mod; 53 } 54 printf("%d\n",(ans%mod+mod)%mod); 55 }
我以爲……我2天打不完式子
我仍是……粘dalao的題解過來吧
可是有幾個要點能夠總結
1.$\left \lfloor \frac{nk}{m}\right \rfloor = \frac{nk-nk\%m}{m} = \frac{nk}{m}-\frac{nk\%m}{m}$
也就是說咱們能夠把m的整數倍提出來
2.這道題大量的運用了把問題拆分再分段處理的思想……這樣的確能夠簡化問題,這種不行就拆分的轉換是很巧妙的
代碼:
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 using namespace std; 5 #define mod 998244353 6 #define N 500010 7 #define K 500000 8 #define LL long long 9 #define db double 10 int n,m,x;double tmp; 11 //把整除拆成 12 inline int Sum(int x){return ((LL)x*(x+1)>>1)%mod;} 13 int prime[N/10],tot,vis[N],mu[N]; 14 inline void intn(int m) 15 { 16 register int i,j,tmp; 17 for(mu[1]=1,i=2;i<=m;++i) 18 { 19 if(!vis[i])prime[++tot]=i,mu[i]=-1; 20 for(j=1;j<=tot&&(tmp=i*prime[j])<=m;++j) 21 { 22 vis[tmp]=1; 23 if(i%prime[j]==0){mu[tmp]=0;break;} 24 mu[tmp]=-mu[i]; 25 } 26 } 27 for(i=2;i<=m;++i)mu[i]+=mu[i-1]; 28 } 29 inline int min(int a,int b){return a<b?a:b;} 30 inline int calc(int a,int b) 31 { 32 register int i,j,ret=0; 33 if(a>b)a^=b,b^=a,a^=b; 34 for(i=1;i<=a;i=j+1) 35 j=min(a/(a/i),b/(b/i)),ret=(ret+(LL)(mu[j]-mu[i-1])*(a/i)*(b/i)%mod)%mod; 36 return ret; 37 } 38 int main() 39 { 40 // freopen("Ark.in","r",stdin); 41 register int i,j,k; 42 scanf("%d%d%lf",&n,&m,&tmp);x=tmp; 43 int ans=((LL)Sum(n)*Sum(m)-(LL)Sum(n)*m-(LL)Sum(m)*n)%mod; 44 if(n>m)n^=m,m^=n,n^=m;intn(n); 45 for(i=1;i<=n;++i) 46 ans=(ans+(LL)(2*i*(x/i)+i)*calc(n/i,m/i)%mod)%mod; 47 ans=(ans+mod)%mod,printf("%lld\n",(LL)ans*499122177%mod); 48 }