牛客小白月赛39 G. 冷静(树状数组/线性筛)

链接:https://ac.nowcoder.com/acm/contest/11216/G
来源:牛客网

题目描述

想去实现宏大的梦想 向着那遥不可及的地方

想在那一片纯白的世界 留下我最初的脚印

在世界的终端 太阳在永不停息地运转

南风终将吹过小岛 轻抚我的柔发

想去实现心底小小的梦

——《ハルカトオク》

痛定思痛,奋楫争先再出发……

q 次询问。每次询问给定 n 和 K,问 1 ~ n 中有多少数可以表示为大于等于 K 的质数的乘积(一个数可以乘多次)。

输入描述:

第一行读入一个整数q 
接下来q行每行读入2个整数,第i行表示 niniKiKi
1≤q≤3e61≤q≤3e6,1≤ni,Ki≤3e61≤ni,Ki≤3e6。

输出描述:

q行,共 q 个数,分别表示 q 次询问的答案。

示例1

输入

复制

1
100 5

输出

复制

32

示例2

输入

复制

2
20 3
10 5

输出

复制

9
2

暴力做法就是把每个数唯一分解掉,每个查询暴力统计。考虑如何优化。首先,1到n每个数的最小质因子可以通过线性筛\(O(n)\)地求出来,但是这样对于每次查询的复杂度也是\(O(n)\)的,需要继续优化。因为整体值域比较小,可以借助树状数组维护,离线处理询问。具体来说,首先读入所有的询问并按照n从小到大进行排序并处理,然后维护一个变量num,每处理到一个询问,当num小于这个询问的k值的时候就不断把num这个数的最小质因子插入树状数组维护的桶然后把num++。最后\(O(log(n))\)查询答案并更新到答案数组。

注意这个题卡了一波cout

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int prime[10000005], primesize, phi[10000005], mn[10000005];
bool isprime[10000005];
int read() {
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return s*w;
}
void getlist(int listsize) {
    for(int i = 1; i <= 3000002; i++) isprime[i] = 1, mn[i] = 0x3f3f3f3f;
    isprime[1] = false;
    for(int i = 2; i <= listsize; i++) {
        if(isprime[i]) prime[++primesize] = i, mn[i] = i;
         for(int j = 1; j <= primesize && i * prime[j] <= listsize; j++) {
            isprime[i * prime[j]] = false;
            mn[i * prime[j]] = min(mn[i * prime[j]], prime[j]);
            if(i % prime[j] == 0) break;
        }
    }
}
struct ask {
	int n, k, id;
	bool operator < (const ask& o) const {
		return n < o.n;
	}
};
int ans[3000005];
int b[3000005];
void modify(int x, int y) {
	for(; x <= 3000000; x += (x & -x)) {
		b[x] += y;
	}
}
int query(int x) {
	int ans = 0;
	for(; x; x -= (x & -x)) {
		ans += b[x];
	}
	return ans;
}
int main() {

	getlist(3000000);
	int q;
	cin >> q;
	vector<ask> v;
	for(int i = 1; i <= q; i++) {
		int n, k;
		n = read(), k = read();
		ask tmp = {n, k, i};
		v.push_back(tmp);
	}
	sort(v.begin(), v.end());
	int num = 2;
	for(auto a : v) {
		while(num <= a.n) {
			modify(mn[num], 1);
			num++;
		}
		ans[a.id] = query(a.n) - query(a.k - 1);
	}
	for(int i = 1; i <= q; i++) {
		printf("%d\n", ans[i]);
	}
	return 0;
}
上一篇:c# 磁盘空间计算 和 目录空间 、 文件大小 计算


下一篇:39.组合总和(回溯算法)