BZOJ.4816.[SDOI2017]數字表格(莫比烏斯反演)
題目鏈接
總感覺博客園的\(Markdown\)很。。\(gouzhi\),可以看這的。
這個好像簡單些啊,只要不犯sb錯誤
\(Description\)
用\(f[i]\)表示\(Fibonacci\)數列的第\(i\)項,求\[\prod_{i=1}^n\prod_{j=1}^mf[\gcd(i,j)]\mod (10^9+7)\]
\(Solution\)
\[ \begin{aligned}
Ans &=\prod_{i=1}^n\prod_{j=1}^mf[(i,j)]\ &=\prod_{d=1}^n\prod_{i=1}^n\prod_{j=1}^m[(i,j)=d]*f[d]\ &=\prod_{d=1}^nf[d]^{\sum_{i=1}^n\sum_{j=1}^m[(i,j)=d]}\ &=\prod_{d=1}^nf[d]^{\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[(i,j)=1]}\ &=\prod_{d=1}^nf[d]^{\sum_{i=1}^{min}\mu(i)\lfloor\frac{n}{id}\rfloor\lfloor\frac{m}{id}\rfloor}\ &=\prod_{d=1}^nf[d]^{\sum_{d\mid T}\mu(\frac{T}{d})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}
\end{aligned}
\]
直接去枚舉\(T\),
\[ \begin{aligned} Ans &=\prod_{d=1}^nf[d]^{\sum_{d\mid T}\mu(\frac{T}{d})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}\ &=\prod_{T=1}^n\prod_{d\mid T}f[d]^{\mu(\frac{T}{d})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}\ &=\prod_{T=1}^n\prod_{d\mid T}(f[d]^{\mu(\frac{T}{d}) })^{\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor} \end{aligned} \]
同之前,枚舉約數暴力更新\(\prod_{d\mid T}f[d]^{\mu(\frac{T}{d})}\),復雜度\(O(n\log n)\)。(\(\prod_{d\mid T}f[\frac{T}{d}]^{\mu(d)}\))
註意次冪不能直接取模,利用費馬小定理可以對\(mod-1\)取模。
然後註意\(f[0]=0\)。。
小結:
套路1:把\(\prod\)換到冪上去,這樣就有\(\sum\)了。
套路2:令\(T=id\),在最外層枚舉\(T\)。
另外常見的就不再寫了。
F[]
用int,FP()
中的長式子放到外面,優化還是比較明顯有的。
//17912kb 35060ms
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define mod (1000000007)
typedef long long LL;
const int N=1e6+2;
int cnt,P[N>>3],mu[N+2],f[N+2],inv_f[N+2],F[N+2];
bool Not_p[N+2];
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
int FP(LL x,int k)
{
LL t=1;
for(; k; k>>=1,x=x*x%mod)
if(k&1) t=t*x%mod;
return t;
}
void Init()
{
mu[1]=f[1]=inv_f[1]=1;//f[0]=0!
for(int i=2; i<N; ++i)
f[i]=(f[i-1]+f[i-2])%mod, inv_f[i]=FP(f[i],mod-2);
for(int i=2; i<N; ++i)
{
if(!Not_p[i]) P[++cnt]=i,mu[i]=-1;
for(int v,j=1; j<=cnt&&(v=i*P[j])<N; ++j)
{
Not_p[v]=1;
if(i%P[j]) mu[v]=-mu[i];
else {mu[v]=0; break;}
}
}
for(int i=0; i<N; ++i) F[i]=1;//F[0]=1
for(int i=1; i<N; ++i)
if(mu[i]==1)
for(int d=1,T=0; (T+=i)<N; ++d) F[T]=1ll*F[T]*f[d]%mod;
else if(mu[i]==-1)
for(int d=1,T=0; (T+=i)<N; ++d) F[T]=1ll*F[T]*inv_f[d]%mod;
for(int i=2; i<N; ++i) F[i]=1ll*F[i]*F[i-1]%mod;
}
inline int inv(int x){
return FP(x,mod-2);
}
int Calc(int n,int m)
{
if(n>m) std::swap(n,m);
int res=1;
for(int nxt,tmp,i=1; i<=n; i=nxt+1){
nxt=std::min(n/(n/i),m/(m/i)),
tmp=1ll*F[nxt]*inv(F[i-1])%mod,//存次數就沒用啦?大概是留一個式子比較好?
res=1ll*res*FP(tmp,1ll*(n/i)*(m/i)%(mod-1))%mod;//註意次冪是模(mod-1)。
}
return res;
}
int main()
{
Init();
int T,n,m; scanf("%d",&T);
while(T--)
scanf("%d%d",&n,&m),printf("%d\n",Calc(n,m));
return 0;
}
BZOJ.4816.[SDOI2017]數字表格(莫比烏斯反演)