zoukankan      html  css  js  c++  java
  • BZOJ.4816.[SDOI2017]数字表格(莫比乌斯反演)

    题目链接

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

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


    (Description)
      用(f[i])表示(Fibonacci)数列的第(i)项,求$$prod_{i=1}^nprod_{j=1}^mf[gcd(i,j)]mod (10^9+7)$$
    (Solution)

    [ egin{aligned} Ans &=prod_{i=1}^nprod_{j=1}^mf[(i,j)]\ &=prod_{d=1}^nprod_{i=1}^nprod_{j=1}^m[(i,j)=d]*f[d]\ &=prod_{d=1}^nf[d]^{sum_{i=1}^nsum_{j=1}^m[(i,j)=d]}\ &=prod_{d=1}^nf[d]^{sum_{i=1}^{lfloorfrac{n}{d} floor}sum_{j=1}^{lfloorfrac{m}{d} floor}[(i,j)=1]}\ &=prod_{d=1}^nf[d]^{sum_{i=1}^{min}mu(i)lfloorfrac{n}{id} floorlfloorfrac{m}{id} floor}\ &=prod_{d=1}^nf[d]^{sum_{dmid T}mu(frac{T}{d})lfloorfrac{n}{T} floorlfloorfrac{m}{T} floor} end{aligned} ]

      直接去枚举(T)

    [ egin{aligned} Ans &=prod_{d=1}^nf[d]^{sum_{dmid T}mu(frac{T}{d})lfloorfrac{n}{T} floorlfloorfrac{m}{T} floor}\ &=prod_{T=1}^nprod_{dmid T}f[d]^{mu(frac{T}{d})lfloorfrac{n}{T} floorlfloorfrac{m}{T} floor}\ &=prod_{T=1}^nprod_{dmid T}(f[d]^{mu(frac{T}{d}) })^{lfloorfrac{n}{T} floorlfloorfrac{m}{T} floor} end{aligned} ]

      同之前,枚举约数暴力更新(prod_{dmid T}f[d]^{mu(frac{T}{d})}),复杂度(O(nlog n))((prod_{dmid T}f[frac{T}{d}]^{mu(d)}))。总复杂度(O(nlog n+Tsqrt 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
    ",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
    ",ans);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    Python动态展示遗传算法求解TSP旅行商问题
    MOEAD算法中均匀权向量的实现---Python
    HDU 5294 多校第一场1007题 最短路+最小割
    POJ 3261 Milk Patterns sa+二分
    HDU 4292 FOOD 2012 ACM/ICPC Asia Regional Chengdu Online
    CodeForces 201A Clear Symmetry
    POJ 1679 The Unique MST 确定MST是否唯一
    POJ 3268 Silver Cow Party 最短路 基础题
    POJ 2139 SIx Degrees of Cowvin Bacon 最短路 水題
    POJ2229 Sumsets 基礎DP
  • 原文地址:https://www.cnblogs.com/SovietPower/p/8718273.html
Copyright © 2011-2022 走看看