炫酷反演魔術課件byVFK
stO FDF Orz(證實全有%%%)php
\(F(n)=\sum\limits_{d|n}f(d)\Rightarrow f(n)=\sum\limits_{d|n}\mu(\frac n d)F(d)\)
\(F(n)=\sum\limits_{n|d}f(d)\Rightarrow f(n)=\sum\limits_{n|d}\mu(\frac d n)F(d)\)
推帶\(\gcd\)的題經常使用式子:(其實是借用了積性函數的式子)
\([\gcd(i,j)==1]=\sum\limits_{d|gcd(i,j)}\mu(d)\)
\(gcd(i,j)=\sum\limits_{d|i,d|j}\varphi(d)\)html
洛谷P3455 [POI2007]ZAP-Queries
\(f(n)=\sum\limits_{i=1}^a\sum\limits_{j=1}^b[\gcd(i,j)==n]\)
1.老實反演
\(F(n)=\sum\limits_{n|d}f(d)=\lfloor\frac a n\rfloor\lfloor\frac b n\rfloor\)
\(f(n)=\sum\limits_{n|d}\mu(\frac{d}{n})\lfloor\frac{a}{d}\rfloor\lfloor\frac{b}{d}\rfloor=\sum\limits_{d=1}^{\lceil\frac{\min(a,b)}{n}\rceil}\mu(d)\lfloor\frac a{nd}\rfloor\lfloor\frac b{nd}\rfloor\)
2.套式子
\(f(n)=\sum\limits_{i=1}^{\lfloor\frac{a}{n}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{b}{n}\rfloor}[\gcd(i,j)==1]=\sum\limits_{d|gcd(i,j)}\sum\limits_{i=1}^{\lfloor\frac{a}{n}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{b}{n}\rfloor}\mu(d)=\sum\limits_{d=1}^{\lfloor\frac{\min(a,b)}{n}\rfloor}\mu(d)\lfloor\frac a{nd}\rfloor\lfloor\frac b{nd}\rfloor\)
整除分塊,時間複雜度\(O(\sqrt n)\)。c++
#include<bits/stdc++.h> #define LL long long #define RG register #define R RG int #define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin)) using namespace std; const int SZ=1<<19,N=50009; int u[N],pr[N],p;bool f[N]; char buf[SZ],*ie=buf+SZ,*ip=ie-1; inline int in(){ G;while(*ip<'-')G; R x=*ip&15;G; while(*ip>'-'){x*=10;x+=*ip&15;G;} return x; } int main(){ u[1]=f[1]=1; for(R i=2;i<N;++i){ if(!f[i])u[pr[++p]=i]=-1; for(R j=1;j<=p&&i*pr[j]<N;++j){ f[i*pr[j]]=1; if(i%pr[j]==0)break; u[i*pr[j]]=-u[i]; } u[i]+=u[i-1]; } for(R n=in();n;--n){ R a=in(),b=in(),d=in(),lim=min(a,b)/d,ans=0; for(R l=1,r;l<=lim;l=r+1){ r=min(a/(a/l),b/(b/l)); ans+=(u[r]-u[l-1])*(a/(d*l))*(b/(d*l)); } printf("%d\n",ans); } return 0; }
洛谷P1829 [國家集訓隊]Crash的數字表格 / JZPTAB
\[\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{\gcd(i,j)}\\=\sum_{k=1}^n\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{k}[\gcd(i,j)==k]\\=\sum_{k=1}^n\sum_{i=1}^\frac{n}{k}\sum_{j=1}^\frac{m}{k}ijk[\gcd(i,j)==1]\\=\sum_{k=1}^nk\cdot\sum_{i=1}^\frac{n}{k}\sum_{j=1}^\frac{m}{k}\sum_{d|\gcd(i,j)}\mu(d)ij\\=\sum_{k=1}^nk\cdot\sum_{d=1}^\frac{n}{k}\mu(d)\sum_{i=1}^\frac{n}{kd}\sum_{j=1}^\frac{m}{kd}d^2ij\\=\sum_{k=1}^nk\cdot\sum_{d=1}^\frac{n}{k}d^2\mu(d)\frac{\frac{n}{kd}(\frac{n}{kd}+1)}{2}\frac{\frac{m}{kd}(\frac{m}{kd}+1)}{2}\]數組
#include<bits/stdc++.h> #define LL long long #define RG register #define R RG int #define YL 20101009 using namespace std; const int N=1e7+9; int p,pr[N/10],mu[N],s[N];bitset<N>np; inline LL Sum(LL x){ return x*(x+1)%YL*((YL+1)>>1)%YL; } LL Calc(R n,R m){ LL ans=0; for(R d=1,r;d<=n;d=r+1){ r=min(n/(n/d),m/(m/d)); ans=(ans+(s[r]-s[d-1]+YL)*Sum(n/d)%YL*Sum(m/d))%YL; } return ans; } int main(){ R n,m;LL ans=0; cin>>n>>m; if(n>m)swap(n,m); s[1]=1; for(R i=2;i<=n;++i){ if(!np[i])pr[++p]=i,mu[i]=-1; s[i]=(s[i-1]+(LL)i*i%YL*mu[i]+YL)%YL; for(R j=1;j<=p&&i*pr[j]<=n;++j){ np[i*pr[j]]=1; if(i%pr[j]==0)break; mu[i*pr[j]]=-mu[i]; } } for(R k=1,r;k<=n;k=r+1){ r=min(n/(n/k),m/(m/k)); ans=(ans+(Sum(r)-Sum(k-1)+YL)*Calc(n/k,m/k))%YL; } cout<<ans<<endl; return 0; }
然鵝BZOJ的JZPTAB有屢次詢問咋辦?
詳細題解by hbyer
有一個新的套路:枚舉重複出現的\(kd\)(令其爲\(t\))
\[\sum_{t=1}^n\frac{\frac{n}{t}(\frac{n}{t}+1)}{2}\frac{\frac{m}{t}(\frac{m}{t}+1)}{2}\sum_{d|t}td\mu(d)\]
後面那個\(\sum_{d|t}td\mu(d)\)當作關於\(t\)的函數,暴力推一推發現是個積性函數,篩一下就行了。ide
洛谷P3704 [SDOI2017]數字表格
跟上一題同樣的套路
\[\prod\limits_{i=1}^n\prod\limits_{j=1}^mFib_{\gcd(i,j)}=\prod\limits_{k=1}^nFib_k^{\sum\limits_{d=1}^n\mu(d)\frac{n}{kd}\frac{m}{kd}}=\prod\limits_{t=1}^n\prod\limits_{k|t}Fib_k^{\mu(\frac{t}{k})\frac{n}{t}\frac{m}{t}}\\=\prod\limits_{t=1}^n\left(\prod\limits_{k|t}Fib_k^{\mu(\frac{t}{k})}\right)^{\frac{n}{t}\frac{m}{t}}\]
中間那塊預處理前綴積,外面數論分塊便可。函數
不經常使用可是好看:\(f(n)=\sum\limits_{i=0}^n(-1)^i\binom{n}{i}g(i)\Rightarrow g(n)=\sum\limits_{i=0}^n(-1)^i\binom{n}{i}f(i)\)
正好難算,至多好算:\(f(n)=\sum\limits_{i=0}^n\binom{n}{i}g(i)\Rightarrow g(n)=\sum\limits_{i=0}^n(-1)^{n-i}\binom{n}{i}f(i)\)
正好難算,至少好算:\(f(k)=\sum\limits_{i=k}^n\binom{i}{k}g(i)\Rightarrow g(k)=\sum\limits_{i=k}^n(-1)^{i-k}\binom{i}{k}f(i)\)post
設\(f(n)\)爲至多\(n\)元素錯排方案數也就是全排列方案數\(n!\),\(g(n)\)爲\(n\)元素錯排方案數。
經過枚舉排列中錯位的元素個數\(i\),有\(f(n)=\sum\limits_{i=0}^n\binom{n}{i}g(i)\)。
直接套第二個式子便可得\(g(n)=\sum\limits_{i=0}^n(-1)^{n-i}\binom{n}{i}f(i)=n!\sum\limits_{i=0}^n(-1)^i\frac{1}{i!}\)spa
洛谷P4859 已經沒有什麼好懼怕的了
套第三個式子。然並卵關鍵要想到如何DP和轉化code
#include<bits/stdc++.h> #define LL long long #define RG register #define R RG int using namespace std; const LL N=2009,YL=1e9+9; int a[N],b[N],ff[N],gg[N],F[N],I[N]; int Pow(LL b,R k,LL a=1){ for(;k;k>>=1,b=b*b%YL) if(k&1)a=a*b%YL; return a; } int main(){ R n,k,ans=0,*f=ff,*g=gg; cin>>n>>k;k=(n+k)/2; for(R i=1;i<=n;++i)cin>>a[i]; for(R i=1;i<=n;++i)cin>>b[i]; sort(a+1,a+n+1); sort(b+1,b+n+1); f[0]=g[0]=F[0]=1; for(R i=1;i<=n;++i){ R pos=lower_bound(b+1,b+n+1,a[i])-b-1; for(R j=1;j<=i;++j) g[j]=(f[j]+(LL)f[j-1]*max(pos-j+1,0))%YL; swap(f,g); } for(R i=1;i<=n;++i)F[i]=(LL)F[i-1]*i%YL; I[n]=Pow(F[n],YL-2); for(R i=n;i;--i)I[i-1]=(LL)I[i]*i%YL; for(R i=k;i<=n;++i) ans=(ans+(LL)((i-k)&1?YL-1:1)*F[i]%YL*I[k]%YL*I[i-k]%YL*F[n-i]%YL*f[i])%YL; cout<<ans<<endl; return 0; }
十分像二項式反演。。。
正好難算,至多好算:\(f(n)=\sum\limits_{i=0}^nS(n,i)g(i)\Rightarrow g(n)=\sum\limits_{i=0}^n(-1)^{n-i}s(n,i)f(i)\)
正好難算,至少好算:\(f(k)=\sum\limits_{i=k}^nS(i,k)g(i)\Rightarrow g(k)=\sum\limits_{i=k}^n(-1)^{i-k}s(i,k)(i)\)
BZOJ4671 異或圖
套第二個式子。然並卵關鍵要想到子集劃分計數和線性基
完整題解:stO yyb Orzhtm
#include<bits/stdc++.h> #define LL long long #define RG register #define R RG int using namespace std; const int S=69,N=19; char str[S];bool g[S][N][N]; int s,n,c[N]; LL ans,lb[S],fac[N]; void calc(R t){ R cnt=0;memset(lb,0,sizeof(lb)); for(R k=1;k<=s;++k){ R p=0;LL now=0; for(R i=1;i<=n;++i) for(R j=i+1;j<=n;++j) if(c[i]!=c[j])now|=(LL)g[k][i][j]<<p++; for(R i=0;i<p;++i) if(1ll<<i&now){ if(lb[i])now^=lb[i]; else{lb[i]=now;++cnt;break;} } } ans+=(1&t?1:-1)*fac[t-1]*(1ll<<(s-cnt)); } void dfs(R x,R t){ if(x>n)return calc(t-1); for(R&i=c[x]=1;i<=t;++i)dfs(x+1,t+(t==i)); } int main(){ cin>>s; for(R k=1;k<=s;++k){ cin>>str;n=(1+sqrt(1+8*strlen(str)))/2; for(R i=1,p=0;i<=n;++i) for(R j=i+1;j<=n;++j) g[k][i][j]=str[p++]&1; } for(R i=fac[0]=1;i<=n;++i)fac[i]=fac[i-1]*i; dfs(1,1); cout<<ans<<endl; return 0; }
\(\max\{S\}=\sum\limits_{T\subseteq S}(-1)^{|T|+1}\min\{T\}\)
\(\min\{S\}=\sum\limits_{T\subseteq S}(-1)^{|T|+1}\max\{T\}\)
在指望意義下仍然成立,即
\(E(\max\{S\})=\sum\limits_{T\subseteq S}(-1)^{|T|+1}E(\min\{T\})\)
\(E(\min\{S\})=\sum\limits_{T\subseteq S}(-1)^{|T|+1}E(\max\{T\})\)
推廣到求第\(k\)大,只有大小\(\ge k\)的子集產生貢獻
\(\max_k\{S\}=\sum\limits_{T\subseteq S}(-1)^{|T|-k}\binom{|T|-1}{k-1}\min\{T\}\)
\(\min_k\{S\}=\sum\limits_{T\subseteq S}(-1)^{|T|-k}\binom{|T|-1}{k-1}\max\{T\}\)
洛谷P3175 [HAOI2015]按位或
裸題,FWT以後套第三個式子便可
#include<bits/stdc++.h> #define R register int using namespace std; double EPS=1e-10,a[1<<20]; int c[1<<20]; int main(){ R n,m,s=0; cin>>n;m=1<<n; for(R i=0;i<m;++i){ cin>>a[i]; if(a[i]>EPS)s|=i; c[i]=c[i>>1]^(1&i); } if(s!=m-1)return puts("INF"),0; for(R i=1;i<m;i<<=1) for(R j=0;j<m;j+=i<<1) for(R k=0;k<i;++k) a[k+j+i]+=a[k+j]; double ans=0; for(R i=1;i<m;++i) if(a[i]>EPS)ans+=(c[i]?1:-1)/(1-a[i^(m-1)]); return printf("%.10lf\n",ans),0; }
洛谷P4707 重返現世
套第五個式子,然並卵關鍵要想到如何DP出容斥係數
#include<bits/stdc++.h> #define LL long long #define RG register #define R RG int #define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin)) using namespace std; const int SZ=1<<19,N=10009,YL=998244353; char buf[SZ],*ie=buf+SZ,*ip=ie-1; int f[N][12];LL inv[N]; inline int in(){ G;while(*ip<'-')G; R x=*ip&15;G; while(*ip>'-'){x*=10;x+=*ip&15;G;} return x; } inline int add(R x,R y){static int z;z=x+y;return z<YL?z:z-YL;} inline int sub(R x,R y){static int z;z=x-y;return z<0?z+YL:z;} int main(){ R n=in(),s=n-in()+1,m=in(); f[0][0]=1; for(R i=1;i<=n;++i){ R p=in(); for(R j=m-p;~j;--j) for(R k=s;k;--k) f[j+p][k]=add(f[j+p][k],sub(f[j][k-1],f[j][k])); } inv[1]=1;LL ans=f[1][s]; for(R i=2;i<=m;++i){ inv[i]=(YL-YL/i)*inv[YL%i]%YL; ans=(ans+f[i][s]*inv[i])%YL; } cout<<ans*m%YL<<endl; return 0; }
感受就跟高維差分/IFWT同樣?
正好難算,至多好算:\(f(S)=\sum\limits_{T\subseteq S}g(T)\Rightarrow g(S)=\sum\limits_{T\subseteq S}(-1)^{|S|-|T|}f(T)\)
正好難算,至少好算:\(f(S)=\sum\limits_{S\subseteq T}g(T)\Rightarrow g(S)=\sum\limits_{S\subseteq T}(-1)^{|T|-|S|}f(T)\)
還有推廣到多重集的子集反演。
設\(\mu(S)=(-1)^{|S|}[S中沒有重複元素]\)
(原來莫比烏斯函數就是把整數看做其質因數集合而得來的反演係數啊)
\(f(S)=\sum\limits_{T\subseteq S}g(T)\Rightarrow g(S)=\sum\limits_{T\subseteq S}\mu(S-T)f(T)\)
\(f(S)=\sum\limits_{S\subseteq T}g(T)\Rightarrow g(S)=\sum\limits_{S\subseteq T}\mu(T-S)f(T)\)
子集卷積:已知\(a,b\),求\(c\)使得\(c_S=\sum\limits_{T\subseteq S}a_Tb_{S-T}\)(注意和高維前綴和不同!)
與高維前綴和的區別主要在於集合大小的限制,只有\(|T|+|S-T|=|S|\)才能產生貢獻。
考慮把數組按集合大小分解,\(C_{|S|,S}=\sum\limits_{T\subseteq S}A_{|T|,T}B_{|S-T|,S-T}\)
因而枚舉\(i,j\),將\(A_i,B_j\)的或卷積貢獻到\(C_{i+j}\)中。最後把\(C\)反演回來就是咱們要的\(c\)。