• 21杭电多校10D


    题目

    给定(n)(k),求小于(n)且不被前(k)个素数整除的个数,(nle 10^{18},kle 16)

    题解

    很容易想到用容斥来解决,但(Tcdot 2^{16})太大,得想办法经可能预处理。

    前8个素数乘积很小数量级为(10^7),可以预处理前(10^7)的答案,从而可以O(1)查询容易数用前8个素数容斥的结果,然后再把剩下(k-8)个素数容斥出来,统计答案。

    即预处理一半,类似dfs的双向搜索,可以将复杂度的指数部分减半。

    可以想到预处理的方法,但没想到怎么用。。。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5 + 10;
    const int M = 1e7 + 10;
    const int INF = 0x3f3f3f3f;
    typedef long long ll;
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    ll pri[N];
    ll m, n;
    int cnt;
    bool isnp[N];
    bool del[M];
    ll pre[M];
    
    void init() {
        isnp[1] = 1;
        for(int i = 2; i < N; i++) {
            if(!isnp[i]) {
                pri[cnt++] = i;
                for(int j = 2 * i; j < N; j += i)
                    isnp[j] = 1;
            }
        }
    }
    
    ll d[N];
    int sgn[N];
    
    int main() {
        IOS;
        init();
        for(int i = 0; i < (1 << 16); i++) {
            ll res = 1;
            sgn[i] = 1;
            for(int j = 0; j < 16; j++) {
                if(i & (1 << j)) {
                    res = res * pri[j];
                    sgn[i] = -sgn[i];
                }
            }
            d[i] = res;
        }
        ll tot = 1;
        for(int i = 0; i < 8; i++) {
            int p = pri[i];
            tot = tot * p;
            for(int j = p; j < M; j += p) del[j] = 1;
        }
        for(int i = 1; i < M; i++) {
            pre[i] = pre[i - 1] + !del[i];
        }
        int t;
        cin >> t;
        while(t--) {
            ll n;
            int k;
            ll ans = 0;
            cin >> n >> k;
            if(k <= 8) {
                for(int i = 0; i < (1 << k); i++) {
                    ans += n / d[i] * sgn[i];
                }
                cout << ans << endl;
            } else {
                ll ret = 0;
                for(int i = 1; i < (1 << (k - 8)); i++) {
                    ll tn = n / d[i << 8];
                    ret += (tn / tot * pre[tot] + pre[tn % tot]) * (-sgn[i << 8]);
                }
                ans = n / tot * pre[tot] + pre[n % tot] - ret;
                cout << ans << endl;
            }
        }
    }
    
  • 相关阅读:
    new delete的内部实现代码
    子串的替换
    求字符串的长度
    TSQL语句学习(四)
    TSQL语句学习(二)
    杭电acm1036
    杭电acm2032
    杭电acm2045
    杭电acm2072
    杭电acm1029
  • 原文地址:https://www.cnblogs.com/limil/p/15172275.html
Copyright © 2020-2023  润新知