maxnumber

1012: [JSOI2008]最大数maxnumber

Time Limit: 3 Sec   Memory Limit: 162 MB
Submit: 2284   Solved: 1039
[ Submit][ Status][ Discuss]

Description

现在请求你维护一个数列,要求提供以下两种操作:1、 查询操作。语法:Q L功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。限制:L不超过当前数列的长度。2、 插入操作。语法:A n功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。限制:n是非负整数并且在长整范围内。注意:初始时数列是空的,没有一个数。

Input

第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所述,满足(0

Output

对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。

Sample Input

5 100
A 96
Q 1
A 97
Q 1
Q 2

Sample Output

96
93

96

评测链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1012

解法1:线段树,即本篇所讲。

解法2:单调队列+并查集:http://blog.youkuaiyun.com/yuyanggo/article/details/8844625

解法3:单调队列+二分查找:http://blog.youkuaiyun.com/yuyanggo/article/details/8845023

我的程序比较复杂一点。

我是先把所有的操作都读入进去,用数组q[]来记录。这样的话,我就可以记录一个n(表示总共的插入次数,即总点数,用来优化线段树的查询区间),记录一个last(表示最后一次询问是第几个操作,优化最后只有操作但无询问的情况)。

然后,我用一个f【】数组记录线段树中,某节点代表的区间中是否有有值被修改(即是否有新节点插入),用布尔变量qa(初始为0)来记录上一个操作是否是插入操作,读入一段连续的插入操作,直到遇到第一个询问操作时,才对线段树中的节点值进行修改。

当然,还的用len来记录当前已插入多少个元素,对于操作Q  L,实际上就是求线段树中区间[len-L+1,len]的最大值。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define maxn (200000+100)
using namespace std;

struct tnode{char s;int l;}q[maxn];//记录操作 
int n=0,len=0,last=0,d;
int tree[maxn*3];//tree[i]记录节点i代表的区间中的最大值 
bool f[maxn*3];//记录区间中是否有节点的值被修改(即是否有新节点插入) 

void init()
{
  freopen("maxnumber.in","r",stdin);
  freopen("maxnumber.out","w",stdout);
}

void readdata()
{
  int m,i;
  scanf("%d%d\n",&m,&d);
  for(i=1;i<=m;i++)
    {
      scanf("%c%d\n",&q[i].s,&q[i].l);
      if(q[i].s=='A')n++;
    }
  for(i=m;i>0;i--)
    if(q[i].s=='Q'){last=i;break;}  
}
//查找区间【i,i】在线段树中的节点位置 
inline int wei(int i)
{
  int p=1,l=1,r=n,m,k;
  while(l<r)
    {
      m=(l+r)>>1,k=p<<1;
      if(i<=m)p=k,r=m;
      else p=k+1,l=m+1;
    }
  return p;  
}
//对区间中子节点值有变化的点进行染色标记 
inline void ranse(int i)
{
  int k=i>>1;
  while(k>1 &&!f[k])
    f[k]=1,k=k>>1;
}
//对线段树中,子节点值有变化的节点的值进行修改 
void build(int p,int l,int r)
{
  int m=(l+r)>>1,k=p<<1;
  if(f[k])build(k,l,m),f[k]=0; 
  if(f[k+1])build(k+1,m+1,r),f[k+1]=0;
  tree[p]=max(tree[k],tree[k+1]);
}
//在区间[pl,pr]中查询被包含在区间【l,r】中的部分的最大值 
int get(int p,int pl,int pr,int l,int r)
{
  if(l<=pl && pr<=r)return tree[p];
  int m=(pl+pr)>>1,k=p<<1;
  if(r<=m)return get(k,pl,m,l,r);
  if(l>m)return get(k+1,m+1,pr,l,r);
  return max(get(k,pl,m,l,r),get(k+1,m+1,pr,l,r)); 
}

void work()
{
  int t=0,i,k;  bool qa=0;
  memset(f,0,sizeof(f));
  for(i=1;i<=last;i++)
    if(q[i].s=='A')
      {
        len++;k=wei(len);  qa=1;
        tree[k]=(q[i].l+t)%d;//感觉还是把tree记录为long long好一点,但既然过了就懒得去修改了 
        ranse(k);
      }
    else
      {
        if(qa && n>1)build(1,1,n);
        t=get(1,1,n,len-(q[i].l)+1,len); qa=0;
        printf("%d\n",t);
      }  
}

int main()
{
  init();
  readdata();
  work();
  return 0;
}


<think>好的,我现在需要解释用户提供的这段Python代码。首先,我要仔细阅读代码,理解每一行的作用,然后逐步拆解它的功能。用户可能想了解这是否是一个素数生成算法,比如埃拉托斯特尼筛法的变种,或者是其他方法。 首先,代码的第一行是maxNumber = int(input('请输入一个大于 2 的自然数:')),这里用户输入一个数字,转换成整数,这应该是用来确定生成素数的上限。接下来,numbers = set(range(2, maxNumber)),这创建了一个从2开始到maxNumber-1的集合,因为range是左闭右开的。集合的特性是元素唯一且无序,这样后续删除元素会比较方便。 然后,m = int(maxNumber**0.5)+1,这里计算了maxNumber的平方根并取整加1。这一步可能和筛选素数有关,因为在埃拉托斯特尼筛法中,只需要检到平方根即可。接下来的primesLessThanM是一个列表推导式,生成从2到m-1之间的素数。这里的条件判断是0 not in [p%d for d in range(2, int(p**0.5)+1)],这其实是判断p是否为素数。对于每个p,检是否存在d(从2到p的平方根取整加1)能整除p,如果没有的话,说明p是素数。这一步可能存在问题,因为当d的范围是range(2, int(p**0.5)+1)时,比如p=4,int(4**0.5)=2,所以range(2,2+1)即2到2,也就是d=2,这时4%2=0,所以0会在列表中,因此4不会被包括进primesLessThanM。这似乎正确,但需要确认是否正确生成了小于m的素数。 接下来是循环for p in primesLessThanM:,然后内部循环for i in range(2, maxNumber//p+1):,这里i从2开始,到maxNumber//p +1。比如,假设maxNumber10,p是2,那么maxNumber//p是5,所以i的范围是2到5+1=6?不,range的上限是maxNumber//p +1,所以i会取2,3,4,5。然后numbers.discard(i*p),也就是删除i乘以p的倍数。例如,当p=2时,i从2到5,所以删除4,6,8,10。这样剩下的就是未被删除的素数。 最后,print(numbers)输出剩下的集合,也就是所有素数。 但这里可能存在一个问题:primesLessThanM是否正确生成了所有小于等于m的素数?因为生成primesLessThanM的方式是遍历p从2到m-1,然后检p是否为素数。但是当maxNumber的平方根很大时,这个过程可能会比较低效,因为对于每个p,都要遍历d到它的平方根。例如,当maxNumber很大时,m也会很大,生成primesLessThanM可能需要较多的计算。 另外,埃拉托斯特尼筛法的常规做法是直接标记倍数,而不是先生成小素数再去删除。因此这段代码可能是一种变体,先找到所有小于m的素数,然后用这些素数来删除它们的倍数,类似于筛法,但分成了两步:首先生成小素数,再用这些小素数去筛选剩下的数。 例如,假设maxNumber=30,那么m=int(30**0.5)+1=5+1=6?不对,因为30的平方根约是5.477,int之后是5,加1得到6。所以primesLessThanM是生成2到5之间的素数,即2,3,5。然后对于每个p(2,3,5),删除它们的倍数i*p,其中i从2开始到maxNumber//p +1。例如,当p=2时,maxNumber//p=30//2=15,所以i从2到15+1=16?不,range(2, 15+1)是2到16(不包括),即i=2到15。这时候i*p是2*2=4,2*3=6,直到2*15=30。所以删除4,6,8,...,30。同样的,当p=3时,maxNumber//3=10,所以i从2到10,删除3*2=6,3*3=9,直到3*10=30。但此时这些数字可能已经被删除了。最后剩下的就是所有素数。 不过,这样的方法是否正确呢?例如,当p=5时,i的范围是2到30//5+1=6+1=7?或者maxNumber//p=6,所以i的范围是2到6+1=7?range(2,6+1)是2到6,因为range的上限是不包含的。比如,maxNumber//p=30//5=6,所以i的范围是2到6+1=7,即range(2,7)即i=2,3,4,5,6。所以i*p是5*2=10,5*3=15,直到5*6=30。这些数会被删除。但比如25是否是5的倍数?当p=5时,i=5的话是5*5=25,会被删除。是的,所以即使p是5,i的范围会包括到i=5,所以25会被删除。 因此,这段代码的正确性可能没问题,因为它先用primesLessThanM中的素数(即小于等于sqrt(n)的素数)来删除它们的倍数,剩下的就是所有素数。这类似于埃氏筛法,但可能更高效,因为只需要用这些较小的素数来筛选。常规的埃氏筛法不需要预先计算这些素数,而是直接从头开始筛选。所以这里的方法可能是一种优化,或者分步实现的方式。 不过,在生成primesLessThanM的时候,是否可能存在错误?例如,当p是平方数时,比如4,它的平方根是2,所以检d的范围是2到2+1=3?即d=2。4%2==0,所以0在列表中,所以不会被加入primesLessThanM。这没问题。再比如p=9,int(9**0.5)=3,所以d的范围是2到3+1=4?即d=2,3。当d=3时,9%3==0,所以同样会被排除。所以生成primesLessThanM的部分是正确的。 因此,整个代码的思路是: 1. 生成从2到maxNumber-1的集合。 2. 找出所有小于等于maxNumber平方根的素数primesLessThanM。 3. 对于每个这样的素数p,删除集合中所有p的倍数(从2*p到最大的不超过maxNumber的倍数)。 4. 剩下的集合中的数字即为所有素数。 这和埃拉托斯特尼筛法的原理相似,因为埃氏筛法也是通过删除每个素数的倍数来筛选素数,但通常的做法是直接遍历,而不是先找到小素数再删除。这里的做法可能效率更高,因为只需要处理小于等于sqrt(maxNumber)的素数的倍数,而较大的素数的倍数已经被前面的步骤删除了。 例如,当maxNumber=30时,primesLessThanM是2,3,5(因为sqrt(30)≈5.47,m=6,所以检2到5中的素数)。然后对于每个p,删除其所有倍数。例如,当处理p=2时,删除4、6、8等;处理p=3时,删除6、9、12等;处理p=5时,删除10、15、20、25、30等。剩下的未被删除的数字就是素数。 需要注意的是,当maxNumber比较小时,比如输入的是3,那么maxNumber是3,range(2,3)的集合是{2}。m=int(3**0.5)+1=1+1=2,所以primesLessThanM是[p for p in range(2,2)],即空列表。这样循环不会执行,所以最终输出{2},正确。但当输入为4时,maxNumber=4,集合是{2,3},m=int(sqrt(4))+1=2+1=3,primesLessThanM是检p在2到2(因为range(2,3)是2),所以p=2。判断2是否为素数:检d的范围是range(2, int(2**0.5)+1)=range(2,1+1)=range(2,2),即空列表,所以[p for p in ...]中的条件0 not in [],因为空列表中没有0,所以条件成立,所以primesLessThanM是[2]。然后循环p=2,i的范围是2到4//2 +1=2+1=3,即i=2和3?因为range(2,3)包括2。所以i*p=2*2=4,3*2=6(但maxNumber=4,所以i的范围是maxNumber//p +1=4//2=2,所以i的范围是range(2,2+1)=2到2(因为range的上限是3,不包括,所以i=2)。所以numbers.discard(4),集合变成{2,3}。最后输出正确。 这说明代码在输入为4时正确输出2和3。而当输入为5时,maxNumber=5,集合是{2,3,4}。m=int(sqrt(5))+1≈2+1=3,所以primesLessThanM是检p从2到2(range(2,3)即p=2),判断2是否为素数,是的。然后循环p=2,i的范围是2到5//2+1=2+1=3,即i=2和3?因为5//2=2,所以i的范围是range(2, 2+1)=range(2,3),即i=2。i*p=4,所以删除4。集合剩下{2,3},正确。 所以这段代码似乎正确实现了筛选素数的功能,不过其步骤可能与常规的埃氏筛法不同。常规的埃氏筛法是从小到大遍历,每遇到一个未被标记的数,就将其倍数全部标记。而这里则是先找到所有小于sqrt(n)的素数,然后用这些素数去删除倍数。这可能在某些情况下更高效,因为只需要处理这些素数,而无需遍历到更大的数。 现在需要将这个过程用中文逐步解释给用户,确保结构清晰,正确可靠。</think>这个代码实现了筛选素数的功能,采用类似埃拉托斯特尼筛法的思想。以下是逐步解释: --- ### **1. 获取用户输入** ```python maxNumber = int(input('请输入一个大于 2 的自然数:')) ``` - 用户输入一个自然数 `maxNumber`,作为筛选素数的上限。 --- ### **2. 初始化候选数集合** ```python numbers = set(range(2, maxNumber)) ``` - 创建一个集合 `numbers`,包含从 2 到 `maxNumber-1` 的所有自然数。 - 集合的特性是元素唯一且无序,方便后续删除操作。 --- ### **3. 计算筛选范围** ```python m = int(maxNumber**0.5) + 1 ``` - 计算 `maxNumber` 的平方根并向上取整加 1,得到 `m`。 - **数学原理**:若一个数 `n` 是合数,则它必有至少一个因子小于等于 `√n`。因此只需检 `≤√maxNumber` 的素数即可筛掉所有合数。 --- ### **4. 生成小于 m 的素数** ```python primesLessThanM = [p for p in range(2, m) if 0 not in [p % d for d in range(2, int(p**0.5) + 1)]] ``` - **功能**:生成所有小于 `m` 的素数。 - **实现逻辑**: 1. 遍历 `p` 从 2 到 `m-1`。 2. 对每个 `p`,检是否存在 `d`(从 2 到 `√p`)能整除 `p`。 3. 如果所有 `d` 都不能整除 `p`(即列表 `[p%d]` 中没有 0),则 `p` 是素数。 --- ### **5. 筛选主过程** ```python for p in primesLessThanM: for i in range(2, maxNumber // p + 1): numbers.discard(i * p) ``` - **功能**:用 `primesLessThanM` 中的每个素数 `p`,删除 `numbers` 中所有 `p` 的倍数。 - **实现逻辑**: 1. 对每个素数 `p`,计算其倍数 `i*p`(`i` 从 2 开始)。 2. 倍数上限为 `maxNumber//p`,即不超过 `maxNumber` 的最大整数倍。 3. 使用 `discard()` 方法从集合中安全删除元素(即使元素不存在也不会报错)。 --- ### **6. 输出结果** ```python print(numbers) ``` - 最终集合 `numbers` 中剩余的数字即为所有素数。 --- ### **代码原理总结** 1. **初始化候选集合**:假设所有数都是素数。 2. **确定筛选范围**:只需检 `≤√maxNumber` 的素数。 3. **生成小素数**:用于后续筛选。 4. **删除合数**:通过删除小素数的倍数,剩余即为素数。 --- ### **示例** 假设输入 `maxNumber=30`: 1. `numbers = {2,3,4,...,29}` 2. `m = int(√30)+1 = 6`,生成 `primesLessThanM = [2,3,5]` 3. 用 `p=2` 删除 `4,6,8,...,30` 用 `p=3` 删除 `6,9,12,...,30` 用 `p=5` 删除 `10,15,20,25,30` 4. 剩余集合为 `{2,3,5,7,11,13,17,19,23,29}`,即所有素数。 --- ### **注意事项** - 代码正确性依赖 `primesLessThanM` 的生成,但该部分实现效率较低(更适合教学演示)。 - 实际应用中,埃拉托斯特尼筛法通常更高效(直接标记倍数,无需预先生成小素数)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值