HDU4135 Co-prime [容斥]

C o p r i m e Co-prime php


D e s c r i p t i o n \mathcal{Description}
求區間 L , R L,R 之間與 N N 互質的數的個數 , L , R < = 1 0 15 , N < = 1 0 9 L,R<=10^{15}, N<=10^9 html


最初想法
N N 分解質因數, 得出 c n t cnt 個不一樣的質因數, 存到 p [ ] p[] 數組中.
利用 容斥 計算 [ 1 , x ] [1,x] 中與 N N 不互質的數的數量 n u m num , 互質的數量則爲 x n u m x-num .
使用 [ 1 , L 1 ] , [ 1 , R ] [1,L-1],[1,R] 的答案再 容斥 一下就能夠了 .web

A t t e n t i o n Attention 數組

  • , N , p [ ] 分解質數到最後時, N可能爲最後的大質數, 須要計入p[]
  • 在容斥找 n u m num 時須要 求解 L C M LCM , 不能莽乘.
  • DFS容斥時須要 判斷是否一個數都沒有選.

D F S DFS 部分:app

void DFS(int k, bool opt, int p_sum, const int &x){
        if(k == cnt+1){
                if(p_sum == 1) return ;
                if(!opt) Tmp_1 -= x/p_sum;
                else Tmp_1 += x/p_sum;
                return ;
        }
        DFS(k+1, opt^1, Lcm(p_sum, p[k]), x);
        DFS(k+1, opt, p_sum, x);
}

正解部分
按以上解法, 將 D F S DFS 換成 二進制枚舉 A C AC 了, 好奇怪.svg

癥結 ↑ spa

二進制枚舉狀態部分:code

for(reg int i = 1; i < 1<<cnt; i ++){
                int p_sum = 1;
                int p_cnt = 0;
                for(reg int j = 1; j <= cnt; j ++)
                        if((1<<j-1) & i) p_cnt ++, p_sum = Lcm(p_sum, p[j]);
                if(p_cnt & 1) Tmp_1 += x/p_sum;
                else Tmp_1 -= x/p_sum;
        }
        return x-Tmp_1;

實現部分
沒什麼好說的.orm

#include<cstdio>
#include<cctype>
#define reg register
typedef long long ll;

const int maxn = 105;

int Test_Cnt;
int N;
int cnt;
int p[maxn];
ll Tmp_1;
ll L;
ll R;

int Gcd(int a, int b){ return !b?a:Gcd(b, a%b); }
int Lcm(int a, int b){ return a/Gcd(a, b)*b; }

ll Calc(ll x){
        if(!x) return 0;
        Tmp_1 = 0;
        for(reg int i = 1; i < 1<<cnt; i ++){
                int p_sum = 1;
                int p_cnt = 0;
                for(reg int j = 1; j <= cnt; j ++)
                        if((1<<j-1) & i) p_cnt ++, p_sum = Lcm(p_sum, p[j]);
                if(p_cnt & 1) Tmp_1 += x/p_sum;
                else Tmp_1 -= x/p_sum;
        }
        return x-Tmp_1;
}

void Work(){
        scanf("%lld%lld%d", &L, &R, &N);
        cnt = 0;
        for(reg int d = 2; d*d <= N; d ++)
                if(N % d == 0){
                        p[++ cnt] = d;
                        while(N%d == 0) N /= d;
                }
        if(N > 1) p[++ cnt] = N; //Attention
        printf("Case #%d: %lld\n", ++ Test_Cnt, Calc(R) - Calc(L-1));
}

int main(){
        int T;
        scanf("%d", &T);
        while(T --) Work();
        return 0;
}
相關文章
相關標籤/搜索