cf 920G List Of Integers 容斥 + 二分 + gcd

传送门

求大于\(x\)的数里面,第\(k\)个与\(p\)互质的数

进行二分,二分数字,那么对于每一个数字\(mid\),可以查询\([x + 1, mid]\)里面有多少个数与\(p\)互质,如果个数是\(k\),那么答案就是\(mid\)

问题转换为求\([1,n]\)有多少个数与\(p\)互质
模板题

\(p\)进行唯一分解定理分解,然后对于质因子进行容斥。最多有\(7\)个质数,那么容斥的时间复杂度是\(O(2^7*7)\)

在容斥里面因为是质数求lcm,所以不用lcm,直接乘就行了。

#include <bits/stdc++.h>
#define ll long long
#define CASE int Kase = 0; cin >> Kase; for(int kase = 1; kase <= Kase; kase++)
using namespace std;
template<typename T = long long> inline T read() {
    T s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) {s = (s << 3) + (s << 1) + ch - 48; ch = getchar();} 
    return s * f;
}
const int N = 1e5 + 5, M = 1e6 + 5, MOD = 1e9 + 7, CM = 998244353, INF = 0x3f3f3f3f;
int pri[N], tot;
void divi(int n){
    for(int i = 2; i * i <= n; i++) {
        if(n % i == 0) {
            pri[++tot] = i;
            while(n % i == 0) n /= i;
        }
    }
    if(n > 1) pri[++tot] = n;
}
ll gcd(ll a, ll b){
    return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b){
    return a / gcd(a, b) * b;
}
ll cal(ll l, ll r, int p){ // [l, r]多少个数与p互质
    ll ans = 0;
    for(int i = 1; i < (1 << tot); i++) {
        int k = 0; ll lc = 1;
        for(int j = 0; j < tot; j++) {
            if(i & (1 << j)) {
                k++; lc = lc * pri[j + 1];
            }
        }
        if(k & 1) ans += r / lc - (l - 1) / lc;
        else ans -= r / lc - (l - 1) / lc;
    }
    return r - l + 1 - ans;
}
bool check(int mid, int x, int p, int k){
    if(mid <= x) return 0;
    int num = cal(x + 1, mid, p);
    return num >= k;
}
void solve(int kase){
    int x = read(), p = read(), k = read();
    tot = 0;
    divi(p);
    int l = 1, r = 1e9;
    while(r - l >= 1) {
        int mid = (l + r) >> 1;
        if(check(mid, x, p, k)) r = mid;
        else l = mid + 1;
    }
    printf("%d\n", r);
}
const bool DUO = 1;
int main(){
    clock_t start, finish; double totaltime; start = clock();
    if(DUO) {CASE solve(kase);} else solve(1);
    finish = clock(); 
    #ifdef ONLINE_JUDGE
        return 0;
    #endif
    printf("\nTime: %lfms\n", (double)(finish - start) / CLOCKS_PER_SEC * 1000);
    return 0;
}

还有一道题,也有点像

求[1, n!]里与\(m!\)互质的数字个数,对一个质数\(p\)取模
传送门

结论:
对于两个正整数 \(m\)\(n,\) 如果 \(n\)\(m\) 的倍数,那么 \(1 \rightarrow n\) 中与 \(m\) 互素的数的个数为\(\frac{n}{m} \phi(m)\)

因为\(gcd(a, b) = gcd(a + k * b, b)\)\(k\)是正整数

那直接用定义法求出\(\varphi(m!)\),线性筛一下质数。\(m!\)的质因子就是\([1,m]\)里的所有质数。

#include <bits/stdc++.h>
#define ll long long
#define CASE int Kase = 0; cin >> Kase; for(int kase = 1; kase <= Kase; kase++)
using namespace std;
template<typename T = long long> inline T read() {
    T s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) {s = (s << 3) + (s << 1) + ch - 48; ch = getchar();} 
    return s * f;
}
const int N = 1e7 + 5, M = 1e6 + 5, MOD = 1e9 + 7, CM = 998244353, INF = 0x3f3f3f3f;
int mod;
int pri[N], tot;
bool vis[N];
ll ksm(ll a, ll b, ll p){
    ll ans = 1; a %= p;
    while(b){
        if(b & 1) ans = ans * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return ans;
}
void pri_table(int n){
    vis[0] = vis[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(!vis[i]) pri[++tot] = i;
        for(int j = 1; j <= tot; j++) {
            if(i * pri[j] > n) break;
            vis[i * pri[j]] = 1;
            if(i % pri[j] == 0) break;
        }
    }
}
int fac[N], inv[N];
void solve(){
    int n = read(), m = read();
    int phi = 1ll * fac[m] * inv[m] % mod;
    int facc = 1ll * fac[n] * ksm(fac[m], mod - 2, mod) % mod;
    int ans = 1ll * facc * phi % mod;
    printf("%d\n", ans);
}
int main(){
    pri_table(N - 5);
    int t = read(); mod = read();
    fac[0] = 1, inv[0] = 1;
    for(int i = 1; i <= N - 4; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
    for(int i = 1; i <= N - 4; i++) {
        if(!vis[i]) {
            inv[i] = ksm(i, mod - 2, mod);
            inv[i] = 1ll * (1 - inv[i] + mod) % mod;
            inv[i] = 1ll * inv[i - 1] * inv[i] % mod;
        }else inv[i] = inv[i - 1];
    }
    for(int i = 1; i <= t; i++) solve();
    return 0;
}
posted @ 2021-02-15 10:18  Emcikem  阅读(51)  评论(0编辑  收藏  举报