博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

BZOJ.4816.[SDOI2017]数字表格(莫比乌斯反演)

题目链接

总感觉博客园的\(Markdown\)很。。\(gouzhi\),可以看这的

这个好像简单些啊,只要不犯sb错误
**[Update] **真的算反演中比较裸的题了...


\(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)}\))。总复杂度\(O(n\log n+T\sqrt n)\)
  注意次幂不能直接取模,利用费马小定理可以对\(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;
}

新写的代码(19.2.13):

/*
17912kb	33888ms
注意本题特殊在是次幂=-=:mu[]可能等于-1,即乘一个F[]的逆元;指数要对mod-1取模。
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define mod 1000000007
#define Mod(x) x>=mod&&(x-=mod)
typedef long long LL;
const int N=1e6+2;

int sum[N],F[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}
inline int FP(int x,int k)
{
	int t=1;
	for(; k; k>>=1,x=1ll*x*x%mod)
		if(k&1) t=1ll*t*x%mod;
	return t;
}
void Init()
{
	static int cnt,P[N>>3],mu[N],invF[N];
	static bool notP[N];
	F[0]=0, F[1]=F[2]=invF[1]=invF[2]=1;
	for(int i=3; i<N; ++i) F[i]=F[i-1]+F[i-2], Mod(F[i]), invF[i]=FP(F[i],mod-2);
	mu[1]=1;
	for(int i=2; i<N; ++i)
	{
		if(!notP[i]) P[++cnt]=i, mu[i]=-1;
		for(int j=1,v; j<=cnt&&(v=i*P[j])<N; ++j)
		{
			notP[v]=1;
			if(i%P[j]) mu[v]=-mu[i];
			else {mu[v]=0; break;}
		}
	}
	for(int i=0; i<N; ++i) sum[i]=1;//sum[0]=1,因为询问的时候要用到*inv(sum[0]) = =
	for(int d=1; d<N; ++d)//可以先枚举这个mu[d],就可以在循环外写if了=v= 
		if(mu[d]==1)
			for(int T=0,i=1; (T+=d)<N; ++i) sum[T]=1ll*sum[T]*F[i]%mod;
		else if(mu[d]==-1)
			for(int T=0,i=1; (T+=d)<N; ++i) sum[T]=1ll*sum[T]*invF[i]%mod;
	for(int i=2; i<N; ++i) sum[i]=1ll*sum[i]*sum[i-1]%mod;
}

int main()
{
	Init();
	for(int T=read(); T--; )
	{
		const int n=read(),m=read();
		LL ans=1;
		for(int i=1,l=std::min(n,m),nxt; i<=l; i=nxt+1)
		{
			nxt=std::min(n/(n/i),m/(m/i));
			ans=ans*FP(1ll*sum[nxt]*FP(sum[i-1],mod-2)%mod,1ll*(n/i)*(m/i)%(mod-1))%mod;
		}
		printf("%lld\n",ans);
	}

	return 0;
}
posted @ 2018-04-04 17:07  SovietPower  阅读(233)  评论(0)    收藏  举报