SP13015 CNTPRIME - Counting Primes
SP13015 CNTPRIME - Counting Primes
CNTPRIME - Counting Primes
题目描述
Tortoise and Achilles are playing the Counting the Primes game. Achilles will give Tortoise some numbers, and some intervals, and then Tortoise needs to count the primes on those intervals. It is an easy game, but Tortoise is doing the counting slowly. Achilles is pissed off, so he has given you the task as you are a good programmer. For a twist, he has changed the game a little bit, that is he will give some intervals for counting the prime as well as he will give some intervals to change the numbers in that interval.
You are given an array of n elements. After that you will be given M commands. They are -
- 0 x y v - you have to change all numbers in the range of x to y (inclusive) to v, where x and y are two indexes of the array.
- 1 x y - output a line containing a single integer which is the number of primes between x and y (inclusive).
The array is indexed from 1 to n.
Input:
Input starts with an integer T (, denoting the number of test cases.
Each case contains two integers **n (1 and q (1 2*10
** ). Then next line, you will be given N integers. After that each of the next q lines will contain a task in one of the following form:
- 0 x y v (1
- 1 x y (1
And the numbers will be in range of [2, 10
Output:
For each case, print the case number first. Then for each query '1 x y', print the number of primes between x and y [inclusively].
Sample Input
Output for Sample Input
1
5 3
78 2 13 12 3
1 1 2
0 4 4 5
1 1 5
Case 1:
1
4
Note:
- Use Faster IO like scanf,printf
- A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself. The first prime numbers are 2,3,5,7,11....2,3,5,7,11,…
Solution
首先先要知道题目要求我们做什么。题目给出了一个长为
既然有区间推平的操作,那为什么不用珂朵莉树试试呢?(如果你不会珂朵莉树,可以看我写过的一篇博客,这里面我详细介绍了珂朵莉树这一种数据结构的基础做法)
那么我们先将数列构造成为珂朵莉树,那么区间推平这一操作是珂朵莉树自带的操作,所以只用考虑如何求解区间质数个数这一问题。
珂朵莉树基础操作:
struct Node{
int l,r;
mutable int v;
Node(int l,int r=0,int v=0) : l(l),r(r),v(v) {}
bool operator< (const Node &a) const{
return l<a.l;
}
};
set<Node> ctlt;
auto split(int pos)//分裂区间
{
auto it=ctlt.lower_bound(Node(pos));
if (it!=ctlt.end() && it->l==pos) return it;
it--;
if (it->r<pos) return ctlt.end();
int l=it->l,r=it->r,v=it->v;
ctlt.erase(it);
ctlt.insert(Node(l,pos-1,v));
return ctlt.insert(Node(pos,r,v)).first;
}
void assign(int l,int r,int v)//区间推平
{
auto itr=split(r+1),itl=split(l);
ctlt.erase(itl,itr);
ctlt.insert(Node(l,r,v));
}
首先可以用质数筛法预处理出 assign
(区间赋值)进行操作,即扫一遍
void prework()//埃筛
{
prime[1]=1;//1不是质数,所以标记上就行
for (int i=2;i<=MAXN;i++)
if (!prime[i])//当前是质数
for (int j=2;j*i<=MAXN;j++) prime[i*j]=1;//当前质数的所有倍数都是合数
}
int query(int l,int r)
{
auto itr=split(r+1),itl=split(l);//类似assign操作
int res=0;//记录[l,r]的区间质数个数
for (auto it=itl;it!=itr;it++)//遍历之间的所有区间
if (!prime[it->v]) res+=it->r - it->l +1;//如果这个区间的值v是质数,那么将会对答案产生r-l+1的贡献
return res;
}
除开这些操作之外还有一点需要注意,就是在每次输出的时候都要记得先输出一个 Case i:
( i
表示第
Code
#include<bits/stdc++.h>
using namespace std;
template<typename T> void read(T &k)//快读模板
{
k=0;T flag=1;char b=getchar();
while (!isdigit(b)) {flag=b=='-'?-1:1;b=getchar();}
while (isdigit(b)) {k=k*10+b-48;b=getchar();}
k*=flag;
}
struct Node{
int l,r;
mutable int v;
Node(int l,int r=0,int v=0) : l(l),r(r),v(v) {}
bool operator< (const Node &a) const{
return l<a.l;
}
};
set<Node> ctlt;
auto split(int pos)//分裂区间
{
auto it=ctlt.lower_bound(Node(pos));
if (it!=ctlt.end() && it->l==pos) return it;
it--;
if (it->r<pos) return ctlt.end();
int l=it->l,r=it->r,v=it->v;
ctlt.erase(it);
ctlt.insert(Node(l,pos-1,v));
return ctlt.insert(Node(pos,r,v)).first;
}
void assign(int l,int r,int v)//区间推平
{
auto itr=split(r+1),itl=split(l);
ctlt.erase(itl,itr);
ctlt.insert(Node(l,r,v));
}
const int MAXN=1e6;
bool prime[MAXN+5];
int n,q,T;
void prework()//埃筛
{
prime[1]=1;//1不是质数,所以标记上就行
for (int i=2;i<=MAXN;i++)
if (!prime[i])//当前是质数
for (int j=2;j*i<=MAXN;j++) prime[i*j]=1;//当前质数的所有倍数都是合数
}
int query(int l,int r)
{
auto itr=split(r+1),itl=split(l);//类似assign操作
int res=0;//记录[l,r]的区间质数个数
for (auto it=itl;it!=itr;it++)//遍历之间的所有区间
if (!prime[it->v]) res+=it->r - it->l +1;//如果这个区间的值v是质数,那么将会对答案产生r-l+1的贡献
return res;
}
int main()
{
read(T);
prework();//筛质数
for (int t=1;t<=T;t++)
{
printf("Case %d:\n",t);//一定记得要输出这里
ctlt.clear();//多组数据记得清空珂朵莉树
read(n),read(q);
for (int i=1;i<=n;i++)
{
int val;read(val);
ctlt.insert(Node(i,i,val));//每个数看作区间[i,i]的值为val
}
for (int i=1;i<=q;i++)
{
int op,x,y,v;
read(op),read(x),read(y);
if (op==0)
read(v),assign(x,y,v);//操作0,即推平操作
else
printf("%d\n",query(x,y));//操作1,即查询操作
}
}
return 0;
}
需要注意珂朵莉树只能在数据随机的情况下使用,否则一旦 assign
操作过少就会导致区间个数过多,时间就会直接 T 飞。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步