• P1198 [JSOI2008]最大数(线段树)


    P1198 [JSOI2008]最大数(线段树)

    题目描述

    现在请求你维护一个数列,要求提供以下两种操作:

    1、 查询操作。

    语法:Q L

    功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。

    限制:L不超过当前数列的长度。(L>=0)

    2、 插入操作。

    语法:A n

    功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。

    限制:n是整数(可能为负数)并且在长整范围内。

    注意:初始时数列是空的,没有一个数。

    输入输出格式

    输入格式:

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

    接下来的M行,每行一个字符串,描述一个具体的操作。语法如上文所述。

    输出格式:

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

    输入输出样例

    输入样例#1: 复制
    5 100
    A 96
    Q 1
    A 97
    Q 1
    Q 2
    
    输出样例#1: 复制
    96
    93
    96
    

    说明

    [JSOI2008]

    本题数据已加强

    分析解答:

    这个题目线段树,树状数组,单调栈,分块等方法都可以做;

    核心是查找一串数中的最大值。

    下面是线段树的解法:

    这道题并不需要提前建树,只要按照输入的顺序挨个添加就好啦

    要是不会线段树的话,可以先去看一下线段树模板1

    运用线段树的算法。首先建树,把所有的节点的值赋成min_int。用[i,j]表示该区间的最大值。

    1)读入Q L操作。用len表示区间的大小,在len+1的位置放入(L+T)%D的值。

    2)读入A n操作。输出区间[len-n+1,len]这个区间中的最大值,并把t的值进行更新。

    得分:100

    时间复杂度:O(nlogn)

    空间复杂度:O(4*n)

    next数组把所有叶子节点的位置都找到了

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 struct tree
     6 {
     7     int l,r,_max;//左右边界和最大值 
     8 }a[800000];//4倍空间 
     9 int n,m,d,x,t,next[200001];
    10 //建树 
    11 void make_tree(int x,int l,int r)
    12 {
    13     a[x].l=l;
    14     a[x].r=r;
    15     //叶子节点 
    16     if(l==r)
    17     {
    18         //这里x是root
    19         //next里面记录的是所有叶子节点的位置,或者说编号 
    20         next[l]=x;
    21         return;
    22         //这里本来是要做数据的初始化的,但是因为现在数据还没加进来,做不了 
    23     }
    24     int mid=(l+r)/2;
    25     //左右子树 
    26     make_tree(x*2,l,mid);
    27     make_tree(x*2+1,mid+1,r);
    28 }
    29 void add(int x)
    30 {
    31     a[next[++n]]._max=(x+t)%d;//这一步就是做叶子节点数据的初始化 
    32     //本来n是0,第一个数是8的位置,那就插到8的位置就好 
    33     int temp=next[n];
    34     //节点发生改变,肯定要更新父亲节点
    35     //比如说第一个节点的位置是8,那么temp就是从8 4 2 1,这样一直更新到root节点 
    36     while(a[temp]._max>a[temp/2]._max)//子节点大于父亲节点才更新 
    37     {
    38         //无论是左右孩子,除2都可以得到父亲 
    39         a[temp/2]._max=a[temp]._max;
    40         temp=temp/2;
    41     }
    42 }
    43 //查询操作 ,这里的x是根节点 ,y是左边界 ,y是我们要查询的边界的左边界 
    44 int q(int x,int y)
    45 {
    46     //包含的情况,因为求最后几个,右边界是固定的 
    47     if(a[x].l>=y) return a[x]._max;
    48     //没有相交的情况 
    49     if(a[x].r<y) return 0;
    50     //相交又不包含的情况 
    51     //左右孩子中的大值 
    52     return max(q(x*2,y),q(x*2+1,y));
    53 }
    54 void print(int m){
    55     cout<<"i"<<"   "<<"next[i]"<<"  "<<endl;
    56     for(int i=1;i<=2*m;i++){
    57         cout<<i<<"   "<<next[i]<<"  "<<endl;
    58     }
    59 }
    60 int main()
    61 {
    62 //    freopen("in.txt","r",stdin); 
    63     cin>>m>>d;
    64     a[1].l=1;
    65     a[1].r=m;
    66     //这里就是左+右除2 
    67     make_tree(2,1,(m+1)/2);
    68     make_tree(3,(m+1)/2+1,m);
    69 //    print(m);
    70     for(int i=1;i<=m;i++)
    71     {
    72         char ch;
    73         cin>>ch;
    74         cin>>x;
    75         //插入操作 
    76         if(ch=='A') add(x);
    77         if(ch=='Q')
    78         {
    79             //查询操作,比如x是2,比如5个操作,因为进行了两次插入操作,所以n就是2,q(1,2-2+1) 
    80             //这里的1是root,而n-x+1是我们要查询的左边界,因为右边界不用管 
    81             t=q(1,n-x+1);
    82             cout<<t<<endl;
    83         }
    84     }
    85 //    print(m);
    86 }
  • 相关阅读:
    利用AspNetPager控件实现数据分页(存储过程)
    System.Reflection
    规范管理提高效率——国内主要api接口文档工具盘点
    文件管理命令
    操作系统磁盘分区
    实体类配置(Entity)
    SpEL语法
    杂乱无章
    从struts2源码学到的技巧
    Spring基于注解的缓存配置
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/8116892.html
Copyright © 2020-2023  润新知