HDU1695 GCD【容斥原理】【欧拉函数】 两种dfs模板

GCD

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 15365    Accepted Submission(s): 5886

 

Problem Description

Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y) = k. GCD(x, y) means the greatest common divisor of x and y. Since the number of choices may be very large, you're only required to output the total number of different number pairs.
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same.

Yoiu can assume that a = c = 1 in all test cases.

 

 

Input

The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 3,000 cases.
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above.

 

 

Output

For each test case, print the number of choices. Use the format in the example.

 

 

Sample Input

2

1 3 1 5 1

1 11014 1 14409 9

 

 

Sample Output

Case 1: 9

Case 2: 736427

 

Hint

For the first sample input, all the 9 pairs of numbers are (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 5), (3, 4), (3, 5).

 

 

 

Source

2008 “Sunline Cup” National Invitational Contest

 

 

Recommend

wangye   |   We have carefully selected several similar problems for you:  1689 1693 1691 1698 1692 

算法分析:

题意:

给你5个整数a、b、c、d、k,在区间[a,b]中选一个数x,在区间[c,d]中选一个数y,使得x和y的公约数为k,即gcd(x,y) = k。现在问题来了:这样的整数对共有多少对。

代码实现:

a<=x<=b,c<=y<=d(其中a,c=1),中有多少对(x,y)(无序的)满足GCDx,y=k,那么可以转化成求(x,y)分别在区间1<=x<=b/k,1<=y<=d/k中有多少对满足GCD(x,y)=1 转换b/=k,d/=k,之后,假设b<=d,那么我们可以分两步来求值:

 

1、求[1,b][1,b]之间有多少对数互质,显然就是phi[1]+phi[2]+...phi[b]即可,用线性求欧拉函数的算法即可!

 

2.[1,b][b+1,d]之间有多少对数互质,可以先求出有多少对数不互质,然后减去即可! 针对于区间[b+1,d]中得某一个数n求区间[1,b]中有多少个数与他不互质(即有公约数)那么可以用容斥原理较好的解决!两步求值之和即为题意所求!

 

为什么要写第二个dfs模板呢,因为我第一个手写的1606ms过的,网上看到一个349ms过得,第二个dfs照着它写的,但竟然1506ms过,很是郁闷,

代码实现:

自己写dfs1

#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<cctype>  
#include<cmath>  
#include<iostream>  
#include<sstream>  
#include<iterator>  
#include<algorithm>  
#include<string>  
#include<vector>  
#include<set>  
#include<map>  
#include<stack>  
#include<deque>  
#include<queue>
using namespace std;
typedef long long ll;
long long ans=0,sum=0,n;
ll fac[105];
int prime[100005],mark[1000005];//prime是素数数组,mark为标记不是素数的数组
int tot,phi[100005];//phi为φ(),tot为1~i现求出的素数个数
void getphi(int N){
    phi[1]=1;//φ(1)=1
    for(int i=2;i<=N;i++){//从2枚举到N
        if(!mark[i]){//如果是素数
            prime[++tot]=i;//那么进素数数组,指针加1
            phi[i]=i-1;//根据性质1所得
        }
        for(int j=1;j<=tot;j++){//从现求出素数枚举
            if(i*prime[j]>N) break;//如果超出了所求范围就没有意义了
            mark[i*prime[j]]=1;//标记i*prime[j]不是素数
            if(i%prime[j]==0){//应用性质2
                phi[i*prime[j]]=phi[i]*prime[j];break;
            }
            else phi[i*prime[j]]=phi[i]*phi[prime[j]];//应用性质3
        }
    }
}

void factor(ll m)
{
    sum = 0;
    ll tmp = m;
    for(ll i = 2; i*i<=tmp; i++)    //官方版的唯一分解定理
    if(tmp%i==0)
    {
        fac[sum++] = i;
        while(tmp%i==0) tmp /=  i;
    }
    if(tmp>1) fac[sum++] = tmp;
}

void dfs(int i,int cnt,int tmp)//i代表数组的下标
{                              //cnt代表几个元素的最小公倍数   
        
    if(cnt&1)                   //容斥原理
        ans+=n/tmp;
    else
        ans-=n/tmp;
    for(int j=i+1;j<sum;j++)
        dfs(j,cnt+1,tmp*fac[j]);
}

int main()
{
    int T;
    cin>>T;
    int t=0;
    getphi(100005);
    while(T--)
    {
        t++;
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if (k == 0)
        {
            printf("Case %d: 0\n",t);
            continue;
        }
        
        b/=k;d/=k;
        if(b>d)
            swap(b,d);
            ll sum1=0;
        for(int i=b+1;i<=d;i++)
        {
            memset(fac,0,sizeof(fac));
            factor(i);
            n=b;
            ans=0;
            for(int j=0;j<sum;j++)
                dfs(j,1,fac[j]);
            sum1+=(n-ans);
        }
        
        for(int i=1;i<=b;i++)
          sum1+=phi[i];
          printf("Case %d: %lld\n",t,sum1);
    }
} 

另一种dfs:

#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<cctype>  
#include<cmath>  
#include<iostream>  
#include<sstream>  
#include<iterator>  
#include<algorithm>  
#include<string>  
#include<vector>  
#include<set>  
#include<map>  
#include<stack>  
#include<deque>  
#include<queue>
using namespace std;
typedef long long ll;
long long ans=0,sum=0,n,temp;
ll fac[105];
int prime[100005],mark[1000005];//prime是素数数组,mark为标记不是素数的数组
int tot,phi[100005];//phi为φ(),tot为1~i现求出的素数个数
void getphi(int N){
    phi[1]=1;//φ(1)=1
    for(int i=2;i<=N;i++){//从2枚举到N
        if(!mark[i]){//如果是素数
            prime[++tot]=i;//那么进素数数组,指针加1
            phi[i]=i-1;//根据性质1所得
        }
        for(int j=1;j<=tot;j++){//从现求出素数枚举
            if(i*prime[j]>N) break;//如果超出了所求范围就没有意义了
            mark[i*prime[j]]=1;//标记i*prime[j]不是素数
            if(i%prime[j]==0){//应用性质2
                phi[i*prime[j]]=phi[i]*prime[j];break;
            }
            else phi[i*prime[j]]=phi[i]*phi[prime[j]];//应用性质3
        }
    }
}

void factor(ll m)
{
    sum = 0;
    ll tmp = m;
    for(ll i = 2; i*i<=tmp; i++)    //官方版的唯一分解定理
    if(tmp%i==0)
    {
        fac[sum++] = i;
        while(tmp%i==0) tmp /=  i;
    }
    if(tmp>1) fac[sum++] = tmp;
}

 
void dfs(int i, int x, int xu, int nu)  //当前第几个素数,已选几个素数,需要几个素数,素数乘
{
    if (x == xu)
    {
        temp+= (ll)n/nu; 
        return;
    }
    if (i==sum)
        return;
    dfs(i + 1, x + 1, xu, nu*fac[i]);
    dfs(i + 1, x, xu, nu);
}
 
ll rong()
{
    ll ret = 0;
    for (int i = 1; i <=sum; i++)
    {
        temp = 0;
        dfs(0, 0, i, 1);
        if (i & 1) ret +=temp;
        else ret -= temp;
    }
    return (ll)n - ret;        //这里的ret是能被它素因子整除的数量,所以必须求反返回
}

int main()
{
    int T;
    cin>>T;
    int t=0;
    getphi(100005);
    while(T--)
    {
        t++;
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if (k == 0)
        {
            printf("Case %d: 0\n",t);
            continue;
        }
        
        b/=k;d/=k;
        if(b>d)
            swap(b,d);
            ll sum1=0;
        for(int i=b+1;i<=d;i++)
        {
            memset(fac,0,sizeof(fac));
            factor(i);
            n=b;
            sum1+=rong();
        }
        
        for(int i=1;i<=b;i++)
          sum1+=phi[i];
          printf("Case %d: %lld\n",t,sum1);
    }
} 

[ Copy to Clipboard ]    [ Save to File]

为什么要写这个呢因为我第一个手写的1606ms过的,网上看到一个349ms过得,第二个dfs照着它写的,但竟然1506ms过,很是郁闷,下面这个为349ms过

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL long long
const int maxn = 1e5 + 5;
int a, b, c, d, e;
int phi[maxn], prime[maxn] = { 0 }, tol = 0;
int nsu[maxn], no;
LL ans, sum;
void init()
{
	phi[1] = 1;
	for (int i = 2; i < maxn; i++)
	if (!phi[i])
	{
		prime[tol++] = i;
		for (int j = i; j < maxn; j += i)
		{
			if (!phi[j])
				phi[j] = j;
			phi[j] = phi[j] / i*(i - 1);
		}
	}
}
 
 
void dfs(int i, int x, int xu, int nu)  //当前第几个素数,已选几个素数,需要几个素数,素数乘积……编程规范真差!
{
	if (x == xu)
	{
		sum += (LL)b / nu; 
		return;
	}
	if (i == no)
		return;
	dfs(i + 1, x + 1, xu, nu*nsu[i]);
	dfs(i + 1, x, xu, nu);
}
 
LL rong()
{
	LL ret = 0;
	for (int i = 1; i <= no; i++)
	{
		sum = 0;
		dfs(0, 0, i, 1);
		if (i & 1) ret += sum;
		else ret -= sum;
	}
	return (LL)b - ret;		//这里的ret是能被它素因子整除的数量,所以必须求反返回
}
 
int main()
{
	init();
	int T,cas = 1;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d%d%d", &a, &b, &c, &d, &e);
		if (e == 0)
		{
			printf("Case %d: 0\n", cas++);
			continue;
		}
		b /= e; d /= e;
		if (b > d) swap(b, d);
		ans = 0;
		for (int i = 1; i <= b; i++)
			ans += (LL)phi[i];
		for (int i = b + 1; i <= d; i++)
		{
			a = i;
			no = 0;
			for (int j = 0; j < tol&&prime[j] * prime[j] <= a&&prime[j] <= b; j++)
			{
				if (a%prime[j] == 0)
				{
					nsu[no++] = prime[j];
					while (a%prime[j] == 0)
						a /= prime[j];
				}
			}
			if (a>1)			//当出现大素数的时候
				nsu[no++] = a;
			ans += rong();
		}
		printf("Case %d: %I64d\n", cas++, ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sdz20172133/article/details/81661844