noip 2009 hankson的趣味游戏

这道题好久就想做了,但是拖了好久好久。。。

这道题主要有两种做法吧;

分割线


第一种,大神的做法,我直接从大神那里拿过来了,可能侵权了。。。


gcd(x,a0)=a1gcd(x,a0)=a1 
⇒{x=k1∗a1a0=k2∗a1⇒{x=k1∗a1a0=k2∗a1

⇒gcd(k1,k2)=1⇒gcd(k1,k2)=1

简单证明:

假设 gcd(k1,k2)≠1gcd(k1,k2)≠1

设 K=gcd(k1,k2)(K≠1)K=gcd(k1,k2)(K≠1) 
⇒{k1=p∗Kk2=q∗K⇒{k1=p∗Kk2=q∗K 
⇒{x=p∗K∗a1a0=q∗K∗a1⇒{x=p∗K∗a1a0=q∗K∗a1 
⇒gcd(x,a0)=K∗a1≠a1⇒gcd(x,a0)=K∗a1≠a1

这个结论与题目条件不符,所以 gcd(k1,k2)=1gcd(k1,k2)=1成立

结合开始的式子,化简,得到 gcd(x/a1,a0/a1)=1gcd(x/a1,a0/a1)=1

把这个结论推广一下,得到结论 PP

对于两个正整数a,ba,b,设 gcd(a,b)=kgcd(a,b)=k,则存在gcd(a/k,b/k)=1gcd(a/k,b/k)=1


2.lcm


lcm(x,b0)=b1lcm(x,b0)=b1 
⇒gcd(x,b0)=x∗b0/lcm(x,b0)=x∗b0/b1⇒gcd(x,b0)=x∗b0/lcm(x,b0)=x∗b0/b1

由结论PP得 
gcd(x/(x∗b0/b1),b0/(x∗b0/b1))=1gcd(x/(x∗b0/b1),b0/(x∗b0/b1))=1 
化简得 
gcd(b1/b0,b1/x)=1gcd(b1/b0,b1/x)=1


3.gcd&&lcm


整理一下得到的重要的式子

{gcd(x/a1,a0/a1)=1gcd(b1/b0,b1/x)=1{gcd(x/a1,a0/a1)=1gcd(b1/b0,b1/x)=1

用心体会这两个式子,发现 xx 是 a1a1的整数倍且是 b1b1 的因子 
这个好像由 gcd 和 lcm 也可以得到?嗯,就这样。 
于是得到了一种解题思路

b√1b1 枚举 b1b1 的因子(也就是 xx ),如果这个数是 a1a1 的整数倍,并且满足那两个式子,ans++

简单一点儿说其实就是推导出一个式子,然后枚举,可惜我太弱哦了,竟然想不出来!!! 

#include<cstdio>
using namespace std;
int gcd(int a,int b) {
    return b==0?a:gcd(b,a%b);
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        int a0,a1,b0,b1;
        scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
        int p=a0/a1,q=b1/b0,ans=0;
        for(int x=1;x*x<=b1;x++) 
            if(b1%x==0){
                if(x%a1==0&&gcd(x/a1,p)==1&&gcd(q,b1/x)==1) ans++;
                int y=b1/x;//得到另一个因子
                if(x==y) continue; 
                if(y%a1==0&&gcd(y/a1,p)==1&&gcd(q,b1/y)==1) ans++;
            }
        printf("%d\n",ans);
    }
    return 0;
}

代码极其简洁而且有力!!! 


第二种则是清北听神牛们讲过,不过有些细节要注意而已 


就是对于每一个数进行质因数分解。

a=2^n1*3^n2*5^n3……

x=2^m1*3^m2……

由于gcd(a,b)=2^min(n1,m1)*……

所以我们可以直接进行一波大大大大模拟!!!

数据范围是2000000000,这里面不超过sqrt(n)个小于sqrt(n)的质数,和一个大于sqrt(n)的(这个数就是分解完剩下的那个)。

线性筛求出50000所有的质数,分解即可;

最后一个可以特殊一波!!!

 附上我极其丑陋的代码!!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=55000;
int vis[55000],p[6500];
int k=0;
void  prim(){
	memset(vis,0,sizeof(vis));
	vis[0]=vis[1]=1;
	for(int i=2;i<=inf;i++){
		if(!vis[i]) p[++k]=i;
		for(int j=1;j<=k&&i*p[j]<inf;j++){
			vis[p[j]*i]=1;
			if(!(i%p[j])) break;
		}
	}
}
typedef long long ll;
ll ans=1;
int main(){
	prim();
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		ans=1;
		int a0,a1,b0,b1;
		cin>>a0>>a1>>b0>>b1;
		for(int j=1;j<=k;j++){
			ll numa0=0,numa1=0,numb1=0,numb0=0;
			if(a0==1&&a1==1&&b0==1&&b1==1) break;
			while(a0%p[j]==0){numa0++;a0/=p[j];}
			while(b0%p[j]==0){numb0++;b0/=p[j];}
			while(a1%p[j]==0){numa1++;a1/=p[j];}
			while(b1%p[j]==0){numb1++;b1/=p[j];}
			if(numa1>numa0||numb1<numb0){
				ans=0;
				break;
			}
			if(numa0==numa1&&numb0==numb1){
				if(numb0<numa0){
					ans=0;break;
				}else ans*=(numb1-numa1+1);
			}
			if(numa0<numa1||numb0>numb1) ans=0;
			if(numa0>numa1&&numb0<numb1&&numa1!=numb1) ans=0;
		}
		if(b1>1){
			ll sss=b1;
			ll numa0=0,numa1=0,numb1=0,numb0=0;
			//if(a0==1&&a1==1&&b0==1&&b1==1) break;
			while(a0%sss==0){numa0++;a0/=sss;}
			while(b0%sss==0){numb0++;b0/=sss;}
			while(a1%sss==0){numa1++;a1/=sss;}
			while(b1%sss==0){numb1++;b1/=sss;}
			if(numa1>numa0||numb1<numb0){
				ans=0;	
			}
			if(numa0==numa1&&numb0==numb1){
				if(numb0<numa0){
					ans=0;
				}else ans*=(numb1-numa1+1);
			}
			if(numa0<numa1||numb0>numb1) ans=0;
			if(numa0>numa1&&numb0<numb1&&numa1!=numb1) ans=0;
		}
	
		cout<<ans<<endl;
	}
	
}

猜你喜欢

转载自blog.csdn.net/weixin_42759194/article/details/81570480