那么我们当我们的求 1~n 的答案时候:
n
的
a
n
s
=
(
n
/
9699690
)
∗
s
m
+
n
n 的 ans=(n/9699690)*sm+n%9699690 的答案
n 的 ans=(n/9699690)∗sm+n 对于 n%9699690 我们是可以预处理出来的,然后我们 O (1) 出答案了。
做法 三(结合前两种方法,拥有优秀的复杂度)
结合上面两种做法:
当 k<=8 的时候随便做。
当 k> 8 的时候,我们可以对第 9~k 个质数进行容斥,而这个容斥是建立在 n 中的数都不是前 8 个质数的基础上进行容斥的,这个正好可以被第二种做法给快速解决!!!
代码
#include<bits/stdc++.h>
using namespace std;#definedbdouble#definelllonglong#definePirpair<int,int>#definefifirst#definesesecond#definepbpush_back#definem_pmake_pair#defineinf0x3f3f3f3f#defineINF0x3f3f3f3f3f3f3f3f/*==========ACMer===========*/#defineullunsigned llconstint M =9699690;//前 8 个质数的乘积
ull p[20]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
ll sum[M +10], sm;//sm 表示 1~M 中不是前 8 个质数的倍数的数字的个数
vector<ull> a[17], b[17];//前 k 个质数中选择偶数个形成的数 存放在 a 中,选择奇数个形成的数存放在 b 中voiddfs(int x, ull y,int z,int k)//由 k 个质数组成的 y,z表示组成 y 的质数的个数{if(x > k){if(z ==1)//由偶数个质数组成{
a[k].pb(y);}else{
b[k].pb(y);}return;}dfs(x +1, y, z, k);//不使用第 x 个质数dfs(x +1, y * p[x],-z, k);//使用第 x 个质数}voidpre_do(){for(int k =1; k <=16; k ++){if(k <=8)//用 dfs 去暴力组合出前 k 个质数所能形成的乘积的值{dfs(1,1,1, k);}else{dfs(9,1,1, k);}}for(int i =1; i <=8; i ++)//标记1~M 中那些数可以被前 8 个质数整除for(int j = p[i]; j <= M; j += p[i])
sum[j]=1;for(int i =1; i <= M; i ++)//求 1~M 中不能被前 8 个质数整除的前缀和
sum[i]= sum[i -1]+(sum[i]==0);
sm = sum[M];}intmain(){pre_do();int T;scanf("%d",&T);while(T --){
ll n, k;scanf("%lld %lld",&n,&k);
ull ans =0;if(k <=8){for(auto x : a[k]){
ans += n / x;}for(auto x : b[k]){
ans -= n / x;}}else{for(auto x : a[k]){
ull t = n / x;
ans +=(t / M)* sm + sum[t % M];}for(auto x : b[k]){
ull t = n / x;
ans -=(t / M)* sm + sum[t % M];}}printf("%lld\n", ans);}return0;}
题目链接题意问我们 1~n 中有多少个数不是前 k 个质数中的任意一个质数的倍数。n <=1e18, k <= 16, 总共有 1e5 组询问。思路做法 一(会超时)如果我们考虑容斥去做:用二进制枚举,设枚举的乘积为 x,去考虑前 k 个质数是否选择,ans+= n/x∗(−1) 形成 x 质数的个数 ans +=~n/x *(-1)^{形成 x 质数的个数}ans+= n/x∗(−1) 形成 x 质数的个数。单次计算的复杂度为:2k∗k2^k * k2