1. 程式人生 > >hdu 1695 GCD 【容斥原理】

hdu 1695 GCD 【容斥原理】

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=1695

題目大意:求(1,b)區間和(1,d)區間裡面gcd(x, y) = k的數的對數(1<=x<=b , 1<= y <= d)。

思路:讓x,y都除上k,就是求gcd(x,y)=1的對數,gcd=1就是兩個數互質,那麼問題就可以轉換成在(1,b/k)和(1,d/k)中互質的數的對數,題意告訴(1,3)和(3,1)為一種情況,所以限制x<=y即可;

要預處理求出所有數的素因數;

答案很大,用long long 記錄答案;

#include<stdio.h>
#include<string.h>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<set>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=1e5+4;

vector<int>prime[N];
int vis[N];
void get_prime()//得到所有數的素因數
{
    memset(vis,0,sizeof(vis));
    for(int i=0;i<N-1;++i)
        prime[i].clear();
    for(int i=2;i<N-1;i+=2)
        prime[i].push_back(2);
    for(int i=3;i<N-1;i+=2)
    {
        if(!vis[i])
        {
            for(int j=i;j<N-1;j+=i)
            {
                vis[j]=1;
                prime[j].push_back(i);
            }
        }
    }
}

int solve(int n,int m)
{
    int sum=0;
    for(int i=1; i<(1<<prime[m].size()); ++i)
    {
        int k=0,pa=1;
        for(int j=0; j<prime[m].size(); ++j)
        {
            if(i&(1<<j))
            {
                k++;
                pa*=prime[m][j];
            }
        }
        int cur=n/pa;
        if(k&1) sum+=cur;
        else sum-=cur;
    }
    return sum;
}

int main()
{
    get_prime();
    int a,b,c,d,k;
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);
        if(k==0)
        {
            printf("Case %d: 0\n",++cas);//注意格式
            continue;
        }
        b/=k;
        d/=k;
        if(b>d) swap(b,d);
        ll ans=0;
        for(int i=1; i<=d; i++)
        {
            int t=min(i,b);//如果此時i<b,就是求區間[1,i]中與i互質的數,
            ans+=t;        //否則就是求區間[1,b]中與i互質的數,因為是為了時x<=y;
              
            ans-=solve(t,i);//1~t之間與i互素的數的個數
        }
        printf("Case %d: %lld\n",++cas,ans);
    }
    return 0;
}