zoukankan      html  css  js  c++  java
  • ●HDU 1695 GCD

    题链:

    http://acm.hdu.edu.cn/showproblem.php?pid=1695

    题解:

    容斥。

    莫比乌斯反演,入门题。

    问题化简:求满足x∈(1~n)和y∈(1~m),且gcd(x,y)=1的(x,y)的对数。

    下文默认$n leq m$


    1.容斥

    (先写了一个的裸的容斥。)

    令$f(k)为gcd(x,y)=lambda k的(x,y)的对数$

    $ANS=f(0种质数的积)-f(1种质数的积)+f(2种质数的积)-cdots+(-1)^mf(m种质数的积)$

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 100500
    using namespace std;
    bool np[MAXN],can[MAXN];
    int T[MAXN];
    void Prime_Sieve(){
    	static int prime[MAXN],pnt;
    	for(int i=2;i<=100000;i++){
    		if(!np[i]) prime[++pnt]=i,T[i]=1,can[i]=1;
    		for(int j=1;j<=pnt&&i<=100000/prime[j];j++){
    			np[prime[j]*i]=1; 
    			T[prime[j]*i]=T[i]+(i%prime[j]!=0);
    			can[prime[j]*i]=can[i]&&(i%prime[j]!=0);
    			if(i%prime[j]==0) break;
    		}
    	}
    }
    long long work(int b,int d){
    	long long ret=1ll*b*d,tmp;
    	for(int i=2;i<=b;i++) if(can[i]){
    		tmp=(T[i]&1?-1:1)*1ll*(b/i)*(d/i);
    		ret+=tmp;
    	}
    	return ret;
    }
    int main(){
    	Prime_Sieve(); int a,b,c,d,k,Case; long long ans;
    	//while(~scanf("%d",&n)) printf("%d 的质数因子有 %d 种
    ",n,T[n]);
    	scanf("%d",&Case);
    	for(int i=1;i<=Case;i++){
    		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
    		if(k==0){printf("Case %d: 0
    ",i); continue;}
    		if(b>d) swap(b,d);
    		ans=work(b/k,d/k);
    		ans-=work(b/k,b/k)/2;
    		printf("Case %d: %lld
    ",i,ans);
    	}
    	return 0;
    }
    

    2.莫比乌斯反演

    令$f(k)为gcd(x,y)=k的(x,y)的对数$

    $F(k)为gcd(x,y)=lambda k的(x,y)的对数$

    显然$F(k)=sum_{k|d}{f(d)},且F(k)=lfloorfrac{n}{k} floor imeslfloorfrac{m}{k} floor$

    那么由莫比乌斯反演公式的形式二(倍数关系那个式子):

    即 $mathbf{f(n)=sum_{n|d}mu(frac{d}{n})F(d),需满足F(n)=sum_{n|d}f(d)}$

    所以我们要求的答案:$f(1)$可以化为如下形式:

    $f(1)=sum_{1|d}mu(frac{d}{1})F(d)$

    $quadquad=sum_{d=1}^{n}mu(d) imeslfloorfrac{n}{d} floor imeslfloorfrac{m}{d} floor$

    感觉还是$O(n)$的呀,和上面的容斥没什么区别呢?

    其实对于这种式子,有一个trick可以把其复杂度优化到$O(sqrt{n})$

    看看这样一个例子:

    $lfloor frac{100}{34} floor=lfloor frac{100}{35} floor=cdots=lfloorfrac{100}{50} floor=2$

    即在向下取整的情况下,$F(k)$函数会有很多段相同的取值,

    所以可以靠这个来把时间优化到$O(sqrt{n})$。

    具体实现看下面代码中的$work()函数$,(学习别人的,很巧妙,很简洁,但是也很迷。。。) 

    /*
    	http://acm.hdu.edu.cn/showproblem.php?pid=1695
    	莫比乌斯反演,入门题。
    	令 f(k)=gcd(x,y)
    */
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 100500
    using namespace std;
    int mu[MAXN],pmu[MAXN];
    void Mobius_Sieve(){
    	static bool np[MAXN]; mu[1]=1; pmu[1]=1;
    	static int prime[MAXN],pnt;
    	for(int i=2;i<=100000;i++){
    		if(!np[i]) prime[++pnt]=i,mu[i]=-1;
    		pmu[i]=pmu[i-1]+mu[i];
    		for(int j=1;j<=pnt&&i<=100000/prime[j];j++){
    			np[prime[j]*i]=1;
    			if(i%prime[j]) mu[i*prime[j]]=-mu[i];
    			else mu[i*prime[j]]=0;
    			if(i%prime[j]==0) break;
    		}
    	}
    }
    long long work(int b,int d){
    	long long ret=0,tmp;
    	for(int i=1,last;i<=b;i=last+1){
    		last=min(b/(b/i),d/(d/i));
    		tmp=1ll*(pmu[last]-pmu[i-1])*(b/i)*(d/i);
    		ret+=tmp;
    	}
    	return ret;
    }
    int main(){
    	Mobius_Sieve(); 
    	int a,b,c,d,k,Case; long long ans;
    	scanf("%d",&Case);
    	for(int i=1;i<=Case;i++){
    		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
    		if(k==0){printf("Case %d: 0
    ",i); continue;}
    		if(b>d) swap(b,d);
    		ans=work(b/k,d/k);
    		ans-=work(b/k,b/k)/2;
    		printf("Case %d: %lld
    ",i,ans);
    	}
    	return 0;
    }
    

    (诶,这个莫比乌斯反演得到的式子和那个容斥的到的好像是一样的!)

  • 相关阅读:
    汉字乱码、加密后结果字符串不一致
    msgpack和TParams互相转换
    unigui监听会话开始和结束
    System.JSON.Builders.pas
    保证最终一致性的模式
    使用 Delta Sharing 协议进行数据共享
    dremio 16 升级问题
    graylog 4.0 运行
    supabase 开源firebase 可选工具
    cube.js 最新playground 说明
  • 原文地址:https://www.cnblogs.com/zj75211/p/8260413.html
Copyright © 2011-2022 走看看