数论题之GCD&LCM于2019/9/24

看到大佬夺CCPC首金,心潮澎湃的我打算向大佬看齐!
ok,基于在组内分管数学题的任务,我开始学习数学题算法。
第一章,GCD&LCM
这个用辗转相除法代码很容易实现

int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
	return a/gcd(a,b)*b;
}

当然我更习惯这样写,还不用考虑a,b的大小

int gcd(int a,int b)
{
	if(a<b)swap(a,b);
	if(a%b==0)return b;
	else return gcd(a%b,b);
}

然后开始刷题!

HDU-1695

emmmm
解析说是要用到莫比乌斯反演,看了一下午没看懂,先放一下
等明天看懂了反演或者试试看容斥原理
莫比乌斯函数要记住结论:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述其中莫比乌斯函数的普通筛代码如下:

void getMu()
{
    int N=maxn;
	for(int i=1;i<N;++i){
		int target=i==1?1:0;
		int delta=target-mu[i];
		mu[i]=delta;
		for(int j=2*i;j<N;j+=i)
			mu[j]+=delta;
	}
}

ac代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int maxn=100005;
int gcd(int a,int b)
{
	if(a<b)swap(a,b);
	if(a%b==0)return b;
	else return gcd(a%b,b);
}

int mu[100050];//莫比乌斯函数值 
void getMu()//莫比乌斯函数普通筛 
{
    int N=maxn;
	for(int i=1;i<N;++i)
	{
		int target;
		if(i==1)target=1;
		else target=0;
		int delta=target-mu[i];
		mu[i]=delta;
		for(int j=2*i;j<N;j+=i)
			mu[j]+=delta;
	}
}



int main()
{
	getMu();
	int a,b,c,d,k;
	int t;
	cin>>t;
	int cntt=1;
	while(t--)
	{
		cin>>a>>b>>c>>d>>k;
		if(k==0)
		{
			cout<<"Case "<<cntt++<<": "<<0<<endl;
			continue;
		}
		long long ans=0;
		long long anss=0;
		if(b>d)swap(b,d);
		b/=k;
		d/=k;
		for(int i=1;i<=b;i++)
		{
			ans+=mu[i]*(long long)(b/i)*(d/i);
		} 
		for(int i=1;i<=b;i++)
		{
			anss+=mu[i]*(long long)(b/i)*(b/i);
		}
		ans-=anss/2;
		cout<<"Case "<<cntt++<<": "<<ans<<endl;
	}
	
}

HDU-1787

题意:找1–N-1中,与N的GCD>1的数的个数
显然欧拉定理
在这里插入图片描述
康康欧拉定理的解释,算了!看不懂,直接代码走起

int Euler(int n)
{
    int ans=n;
    for(int i=2;i*i<=n;i++)
    {
    	if(n%i==0)
    	{
    		n/=i;
    		ans-=ans/i;
		}
		while(n%i==0)n/=i;
	}
	if(n>1)ans-=ans/n;
	return ans;
}

HDU-2504

水题

#include<iostream>
using namespace std;
//水题 
int gcd(int a,int b)
{
	if(a<b)swap(a,b);
	if(a%b==0)return b;
	else return gcd(a%b,b);
}
int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		int a,b,c;
		cin>>a>>b;
		//gcd(a,c)==b;
		//b|a;b|c
		int x=a/b;
		int k;
		for(int i=2;;i++)
		{
			if(gcd(i,x)==1)
			{
				k=i;
				break;
			}
		}
		cout<<k*b<<endl;
	}
}

HDU-2588

题意:输入N,M(2<=N<=1000000000, 1<=M<=N), 设1<=X<=N,求使gcd(X,N)>=M的X的个数。
题解:假设N=pd,X=qd.若n与x的最大公约数为d,则能够推出p与q肯定是互质的,因为X<=N所以要求的就是p的欧拉函数值了,那么我们就转化成求满足:N=p*d,并且d>=N的p的欧拉函数值之和了。

int solve(int n)
{
	int ans=0;
    for(int i=1;i*i<=n;i++)
    {
    	if(n%i!=0)continue;
    	if(i>=m&&i!=sqrt(n))
    	{
    		ans+=Euler(n/i);
		}
		if(n/i>=m)
		{
			ans+=Euler(i);
		}
	}
	return ans;
}

直接暴力搜一遍因子就可以,不会超时。

HDU-3071

题意:

给定一个长度为n的序列m次操作,操作的种类一共有三种

查询 
    L :查询一个区间的所有的数的最小公倍数
    G :查询一个区间的所有的数的最大公约数
修改 
    C :将给定位置的值修改成

考虑到有单点修改和区间查询,考虑线段树或者树状数组的解法。

在这里插入代码片

等我学会了线段树再看…

HDU-4675

题意:

给一个序列 {an}(ai <= M),问有多少种 {bn} ,满足恰好 k 个位置与 {an} 不同,且 gcd of {bn} = d(1<= d <=M)。
看题解,又是要用到莫比乌斯反演的节奏?
很是令人头大…

#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 300003
#define M 1000000007
typedef long long ll;
using namespace std;

int a[maxn],f[maxn],n,m,k,ai,tep;
ll P[maxn],Q[maxn];

ll power(ll x,ll y)
{
    if (x==0) return 0;
    if (x==1 || y==0) return 1;
    ll tot=1;
    while (y)
    {
        if (y&1) tot=(tot*x)%M;
        y>>=1;
        x=(x*x)%M;
    }
    return tot;
}

ll C(ll x,ll y)
{
    if (y==0 || x==y) return 1;
    ll tot=(P[x]*Q[y])%M;
    tot=(tot*Q[x-y])%M;
    return tot;
}

int main()
{
    ll cur,tot,num;
    Q[1]=P[0]=1;
    for (int i=1; i<maxn; i++) P[i]=(P[i-1]*i)%M,Q[i]=power(P[i],M-2);
    while (scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        for (int i=1; i<=m; i++) a[i]=f[i]=0;
        for (int i=1; i<=n; i++)
        {
            scanf("%d",&ai);
            a[ai]++;
        }
        for (int i=m; i>0; i--)
        {
            cur=0; tot=1; num=m/i;
            for (int j=i; j<=m; j+=i) cur+=a[j];
            if (n-cur>k)
            {
                f[i]=0;
                continue;
            }
            tot=power(num,n-cur); 
            if (n-cur!=k)
            {
                tep=(C(cur,k+cur-n)*power(num-1,k+cur-n))%M;
                tot=(tot*tep)%M;
            }
            f[i]=tot;
            for (int j=i+i; j<=m; j+=i)
            {
                f[i]-=f[j];
                if (f[i]<0) f[i]+=M;
            }
        }
        printf("%d",f[1]);
        for (int i=2; i<=m; i++) printf(" %d",f[i]);
        printf("\n");
    }
    return 0;
}
   (http://www.cnblogs.com/lochan)

这一段直接用组合数学,还是太菜了啊…

HDU-4676

在这里插入代码片

HDU-2104

题意:n个人围成的一个圈,其中有一个人的盒子里面有一个漂亮的帽子,haha从1号开始找,每隔m-1个人找一次,问他最后是否一定能够找到这个帽子。
题解:一定找到这个帽子的话就每个人都要找一遍,n,m互素.
水题

HDU-4497

题意:告诉了三个数的最大公约数和最小公倍数,要你求出这三个数字有多少种可能(次序不同,排列不同)
显然可以发现,当m%n!=0时,即最小公倍数不是最大公约数的倍数关系时,不存在这三个数。
当m%n==0时,令x=m/n,由正数分解定理可知,x=p1^t1 * p2^t2 * ````* pn^tn,显然,对于每一个pi,三个书中有一个对应指数必须为ti,一个对应指数为0,另外一个对应指数从0到ti任意,组合数为6ti。综上解为6t16t2*…6tn。每一个p,t用分解法贪心,唯一要注意的是,x可能为素数,最后要判断x经过连除之后是否为0,在这WA了三次…

#include<iostream>
using namespace std;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n,m;
		cin>>n>>m;
		if(m%n)
		{
			cout<<0<<endl;
			continue;
		}
		else
		{
			int daan=1;
			int x=m/n;			
			if(x==1)
			{
				cout<<1<<endl;
			}
			else 
			{
				int xx=x;
				for(int i=2;i*i<=xx;i++)
				{
					int ans=0;
					if(x%i==0)
					{
						while(x%i==0)
						{
							x/=i;
							ans++;
						}					 
						daan*=(6*ans);
					}		
				}
				if(x!=1)daan*=6;
				cout<<daan<<endl;
			}
		}
	}
}

HDU-5019

题意:求两个数第k大的公因子
题解:求两个数最大公约数的第k大公因子
ps.我是真的没想到这么简单…
菜哭辽…

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long LL;
LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
LL p[10005];
LL cmp(LL a,LL b){
    return a>b;
}
int main()
{
    int tcase;
    scanf("%d",&tcase);
    while(tcase--){
        LL a,b,k;
        scanf("%lld%lld%lld",&a,&b,&k);
        LL d = gcd(a,b);
        int id = 0;
        for(LL i=1;i*i<=d;i++)
        { 
            if(d%i==0){
                 if(i*i==d) p[id++]=i;
                 else{
                    p[id++]=i;
                    p[id++]=d/i;
                 }
            }
        }
        if(id<k){
            printf("-1\n");
        }
        else{
            sort(p,p+id,cmp);
            printf("%lld\n",p[k-1]);
        }
    }
    return 0;
}

先看这几条,明天接着刷。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值