[HDU](1695)GCD ---- 欧拉函数★ + 容斥原理★

版权声明:本文为博主原创文章,转载请预先通知博主(〃'▽'〃)。 https://blog.csdn.net/m0_37624640/article/details/81813322

题目链接

  • 感慨:发现欧拉函数真的在数论中非常容易用到,它真的不仅仅是它定义的那样简单,它有很多妙用!
  • 做法:题意向我们保证a = c =1;
  • 所谓问题就变成了 有多少对(x,y)使得gcd(x,y) = k 并且x ∈[1, b], y ∈ [1, d]
  • 利用欧拉函数思想,可以把gcd(x,y) = k 转化成 gcd(x/k,y/k) = 1 即找互质对数
  • 我们可以把b设为小的那个,保障答案的唯一性,即gcd(3,1) = gcd(1,3)
  • 现在分情况讨论:
  • ①:当y∈[1,b/k]时,即y/k我们发现答案就是phi[1]+phi[2]+……+phi[b/k],因为欧拉函数的性质
  • ②:当y∈[b/k,d/k]时,就不能使用欧拉函数了。
  • 这时候就要考虑如何求
  • y∈[b/k,d/k]中与[1,b/k] 互质对数有多少对,我们可以先求这个答案的逆,即y的质数因子在1~b/k范围内能整除的个数
  • PS:这里需要预处理好y的质因数
  • 咦~我们发现酱紫不就可以使用容斥原理了嘛。
  • y的质数因子在1~b/k范围内能整除的个数 = 每个质因子的倍数的个数-两个质因子乘积的倍数的个数+三个……
  • 然后互质个数就是b/k 减去 y的质数因子在1~b/k范围内能整除的个数 
  • 看了一波kuangbin大大的模拟队列实现容斥感觉不错,接过来学习了~

AC代码:KuangBin版的容斥

#include<bits/stdc++.h>
#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); i++)
#define rev(i,t,s) for(int i = (int)(t); i >= (int)(s); i--)
#define pb(x) push_back(x)
#define all(x) x.begin(),x.end()
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const double PI = 4*atan(1.0);
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
int phi[maxn],prime[maxn];
int que[maxn];
int tot;
vector<int> v[maxn];
void phi_table()//线性筛法欧拉函数表
{
    phi[1] = 1;
    for(int i=2;i<maxn;i++){
        if(!phi[i]){
            phi[i] = i-1;
            prime[tot++] = i;
        }
        for(int j=0;j<tot && 1LL*i*prime[j]<maxn;j++){
            if(i%prime[j]) phi[i*prime[j]] = phi[i]*(prime[j]-1);
            else{
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
        }
    }
}
void dec(int N)//质因数分解
{
    for(int i=2;i<=N;i++) v[i].clear();
    for(int i=2;i<=N;i++){
        int t = i;
        for(int j=2;j*j<=t;j++){
            if(t%j == 0){
                v[i].push_back(j);
                while(t%j == 0) t/=j;
            }
        }
        if(t>1) v[i].push_back(t);
    }
}
ll cal(int n,int x) //数组模拟队列
{
    int num = 0;
    que[num++] = -1;
    for(int i=0;i<(int)v[x].size();i++)
    {
        int e = v[x][i];
        if(e>n) break;
        int k = num;
        for(int j=0;j<k;j++)
        {
            que[num] = que[j]*e*(-1);
            num++;
        }
    }
    ll sum = 0;
    for(int i=1;i<num;i++)
    {
        sum = sum+(n/que[i]);
    }
    return 1LL*n-sum;
}
int main()
{
    #ifdef LOCAL_FILE
    freopen("in.txt","r",stdin);
    #endif // LOCAL_FILE
    ios_base::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int t,id = 1;
    phi_table();
    dec(maxn-2);
    cin>>t;
    while(t--)
    {
        int a,b,c,d,k;
        cin>>a>>b>>c>>d>>k;
        if(k == 0 || k>b || k>d)
        {
            cout<<"Case "<<id++<<": "<<0<<endl;
            continue;
        }
        if(b>d) swap(b,d);
        ll ans = 0;
        b/=k;
        d/=k;
        for(int i=1;i<=b;i++) //求出[1,b/k]内的对数
            ans+=phi[i];
        for(int i=b+1;i<=d;i++)
        {
            ans += cal(b,i);
        }
        cout<<"Case "<<id++<<": "<<ans<<endl;
    }
    return 0;
}

AC代码:枚举子集的容斥(跟大佬们学习的~)

#include<bits/stdc++.h>
#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); i++)
#define rev(i,t,s) for(int i = (int)(t); i >= (int)(s); i--)
#define pb(x) push_back(x)
#define all(x) x.begin(),x.end()
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const double PI = 4*atan(1.0);
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
int phi[maxn],prime[maxn];
int que[maxn];
int tot;
vector<int> v[maxn];
void phi_table()//线性筛法欧拉函数表
{
    phi[1] = 1;
    for(int i=2;i<maxn;i++){
        if(!phi[i]){
            phi[i] = i-1;
            prime[tot++] = i;
        }
        for(int j=0;j<tot && 1LL*i*prime[j]<maxn;j++){
            if(i%prime[j]) phi[i*prime[j]] = phi[i]*(prime[j]-1);
            else{
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
        }
    }
}
void dec(int N)//质因数分解
{
    for(int i=2;i<=N;i++) v[i].clear();
    for(int i=2;i<=N;i++){
        int t = i;
        for(int j=2;j*j<=t;j++){
            if(t%j == 0){
                v[i].push_back(j);
                while(t%j == 0) t/=j;
            }
        }
        if(t>1) v[i].push_back(t);
    }
}
ll cal(int n,int x) //枚举子集的容斥原理
{
    int tmp,flag;
    int sum = 0;
    for(int i=1;i<(1<<v[x].size());i++)
    {
        tmp = 1; flag = 0;
        for(int j=0;j<(int)v[x].size();j++)
        {
            if(i&(1<<j)){ //判断当前情况有多少个质因子
                flag++;
                tmp = tmp*v[x][j];
            }
        }
        if(flag&1) sum += n/tmp; //奇加 (如果有奇数个质因子)
        else sum-= n/tmp; //偶减  (如果有偶数个质因子)
    }
    return 1LL*n-sum;//逆逆得正( ̄▽ ̄)/
}
int main()
{
    #ifdef LOCAL_FILE
    freopen("in.txt","r",stdin);
    #endif // LOCAL_FILE
    ios_base::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int t,id = 1;
    phi_table();
    dec(maxn-2);
    cin>>t;
    while(t--)
    {
        int a,b,c,d,k;
        cin>>a>>b>>c>>d>>k;
        if(k == 0 || k>b || k>d)
        {
            cout<<"Case "<<id++<<": "<<0<<endl;
            continue;
        }
        if(b>d) swap(b,d);
        ll ans = 0;
        b/=k;
        d/=k;
        for(int i=1;i<=b;i++) //求出[1,b/k]内的对数
            ans+=phi[i];
        for(int i=b+1;i<=d;i++)
        {
            ans += cal(b,i);
        }
        cout<<"Case "<<id++<<": "<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_37624640/article/details/81813322