题目
给定(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;
}
}
}