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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值