hdu1695 GCD(容斥)

题意:
给你两个区间 [ a , b ] [a,b] [ b , d ] [b,d] ,再给你一个 k k ,默认 a = b = 1 a=b=1 (那为什么还给a,b,直接给b,d不就好了吗,专门坑我这种读题不仔细的人…),然后问你 g c d ( x , y ) = k gcd(x,y)=k ,其中 1 < = x < = b , 1 < = y < = d 1<=x<=b,1<=y<=d 有多少对。(3,5)和(5,3)是一种。
思路
让求 ( x , y ) = k (x,y)=k ,转换一下即求 ( x / k , y / k ) = 1 (x/k,y/k)=1 ,不就是求两个区间互质的有多少对嘛,直接上容斥,等等…结果不大对,唔,(3,5)和(5,3)算一种,那可怎么办才好,得不重不漏才行呐,看别人题解恍然大悟(莫比乌斯滚蛋)。
对于 [ 1 , b ] [ 1 , d ] [1,b]和[1,d] (假设b<=d) ,我们可以将大区间 分为两个部分即 [ 1 , b ] + [ b + 1 , d ] [1,b] + [b+1,d]
对于[1,b]和[1,b]求互质对,就是求 i = 1 b p h i [ i ] \sum_{i=1}^{b}phi[i] (想想是不是)
对于[1,b]和[b+1,d]这一部分用容斥直接算就行,不会产生重复对。
妙哉!
A C   C o d e AC \ Code

#include <iostream>
#include <cstring>
#include <queue>
#include <cmath>
#include <map>
#include<vector>
#include <stack>
#include <cstdio>
#include <sstream>
#include <algorithm>

using namespace std;
#define read(x) scanf("%d", &x)
#define Read(x, y) scanf("%d%d", &x, &y)
#define gc(x) scanf(" %c", &x);
#define mmt(x, y) memset(x, y, sizeof x)
#define write(x) prllf("%d\n", x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod 998244353
const ll N = 1e5 + 5;
ll m , n;
vector<ll> fac[N+1000];
bool vis[N+1000];
void prime(){
   vis[1] = 1;
   for(ll i = 2;i <= N;++i)if(!vis[i]){
      fac[i].push_back(i);
      for(ll j = i + i;j <= N;j += i) {vis[j] = 1;fac[j].push_back(i);}
   }
}
int phi[N + 1000], prime1[N + 1000];
int tot;//tot计数,表示prime1[N]中有多少质数 
void Euler(){
    phi[1] = 1;
    for(int i = 2; i < N; i ++){
        if(!phi[i]){
            phi[i] = i-1;
            prime1[tot ++] = i;
        }
        for(int j = 0; j < tot && 1ll*i*prime1[j] < N; j ++){
            if(i % prime1[j]) phi[i * prime1[j]] = phi[i] * (prime1[j]-1);
            else{
                phi[i * prime1[j] ] = phi[i] * prime1[j];
                break;
            }
        }
    }
}
 
int solve(ll n){
   ll ans = 0;
   //if(n == 1) {ans += m;return ;}
   ll k = fac[n].size();
   ll s = 1<<k;
   for(ll i = 1;i < s;++i){
      ll cnt  = 0;
      ll p = 1;
      for(ll j = 0;j < k;++j ){
         if((i >> j) & 1) {cnt ++;p *= fac[n][j];}
      }
      if(cnt & 1) ans +=  m/p;
      else ans -=  m/p;
   }
   return m - ans;
}
int main(){
   ll t;
   ll a,b,c,d;
   ll k;
   prime();
   Euler();
   cin >> t;
   for(int Case = 1;Case <= t;Case ++ ){
      cin >> a >> b >> c >> d >> k;
      if(k == 0) {printf("Case %d: %lld\n",Case,0);continue;}
      n = b,m = d;
      n /= k;m /= k;
      if(m > n) swap(n,m);
      ll ans = 0;
      for(ll i = m + 1;i <= n;++i){
         ans += solve(i) ;
      }
      for(int i = 1;i <= m;++i) ans += phi[i];
      printf("Case %d: %lld\n",Case,ans);
   }
}


发布了632 篇原创文章 · 获赞 27 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43408238/article/details/104071713