zoukankan      html  css  js  c++  java
  • ACM——A Simple Problem with Integers(线段树的精华版)

    Description

    给出了一个序列,你需要处理如下两种询问。

    "C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000)。

    "Q a b" 询问[a, b]区间中所有值的和。

    Input

    第一行包含两个整数N, Q。1 ≤ N,Q ≤ 100000.

    第二行包含n个整数,表示初始的序列A (-1000000000 ≤ Ai ≤ 1000000000)。

    接下来Q行询问,格式如题目描述。

    Output

    对于每一个Q开头的询问,你需要输出相应的答案,每个答案一行。

    Sample Input

    10 5
    1 2 3 4 5 6 7 8 9 10
    Q 4 4
    Q 1 10
    Q 2 4
    C 3 6 3
    Q 2 4

    Sample Output

    4
    55
    9
    15

    解释:此题难点主要是如何既能做到能及时更新一片区域的数值而又不会超时(因为每次更新成绩都要费好多时间的,所以这题
    就要用到线段树的精华思想了:延迟标记


    延迟标记主要是帮忙标记下某段数值是否曾经更新过,如果有更新过,那么只有在访问这段区间的时候,延迟标记才会把他积累的
    数值传给下面的节点,起到节省时间的作用。

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<iostream>
      4 
      5 using namespace std;
      6 
      7 const int MAXN=500000;
      8 
      9 struct student
     10 {
     11     int left,right;
     12     __int64 nSum;
     13     __int64 Mark;
     14 }segTree[MAXN*4];
     15 __int64 num[MAXN];   //__int64是Windows专用的专门用于记录超大数据的类型,防止越界
     16 
     17 
     18 
     19 void Build(int index,int l,int r)     //建树
     20 {
     21     segTree[index].left=l;
     22     segTree[index].right=r;
     23     if(segTree[index].left==segTree[index].right)
     24     {
     25         segTree[index].nSum=num[l-1];
     26         return ;
     27     }
     28     int mid=(l+r)/2;
     29     Build(2*index,l,mid);
     30     Build(2*index+1,mid+1,r);
     31     segTree[index].nSum=segTree[index*2].nSum+segTree[index*2+1].nSum;           //每个节点和
     32 }
     33 
     34 
     35 
     36 
     37 
     38 void Add(int i,int left,int right,int b)
     39 {
     40     if(segTree[i].left>=left&&segTree[i].right<=right)
     41     {
     42         segTree[i].nSum+=(segTree[i].right-segTree[i].left+1)*b;//倍数加
     43         segTree[i].Mark+=b;//用+=原因是某段可能会被多次标记
     44         return ;
     45     }    
     46     else
     47     {
     48         if(segTree[i].Mark)                   //最关键部分就是这个标记下传,将增加值val传给他的左右孩子
     49         {
     50             segTree[i*2].nSum+=segTree[i].Mark*(segTree[i*2].right-segTree[i*2].left+1);
     51             segTree[i*2].Mark+=segTree[i].Mark;
     52             segTree[i*2+1].nSum+=segTree[i].Mark*(segTree[i*2+1].right-segTree[i*2+1].left+1);
     53             segTree[i*2+1].Mark+=segTree[i].Mark;
     54             segTree[i].Mark=0;
     55         }
     56         if(right>=segTree[i*2+1].left)
     57             Add(2*i+1,left,right,b);
     58         if(left<=segTree[i*2].right)
     59             Add(2*i,left,right,b);    
     60 
     61         segTree[i].nSum=segTree[i*2].nSum+segTree[i*2+1].nSum;
     62     }
     63 
     64 
     65 }
     66 
     67 
     68 
     69 __int64 Query(int i,int left,int right)
     70 {
     71     if(segTree[i].left>=left&&segTree[i].right<=right)
     72             return segTree[i].nSum;
     73     if(segTree[i].Mark)         //求和同样需要标记下传
     74         {
     75             segTree[i*2].nSum+=segTree[i].Mark*(segTree[i*2].right-segTree[i*2].left+1);
     76             segTree[i*2].Mark+=segTree[i].Mark;
     77             segTree[i*2+1].nSum+=segTree[i].Mark*(segTree[i*2+1].right-segTree[i*2+1].left+1);
     78             segTree[i*2+1].Mark+=segTree[i].Mark;
     79             segTree[i].Mark=0;
     80         }
     81         int mid=(segTree[i].left+segTree[i].right)/2;
     82         __int64 max1=0,max2=0;
     83     if(mid<left)
     84             max1= Query(2*i+1,left,right);
     85     else
     86         if(right<=mid)
     87                 max1=Query(2*i,left,right);
     88         else
     89             {
     90                 max1=Query(2*i,left,mid);
     91                 max2=Query(2*i+1,mid+1,right);
     92             }
     93     return max1+max2;
     94 
     95 
     96 
     97 }
     98 
     99 
    100 
    101 
    102 
    103 int main()
    104 {
    105     int n,t;
    106     int x,y,z;
    107 
    108     char C;
    109     while(scanf("%d%d",&n,&t)!=EOF)
    110     {
    111         memset(segTree,0,sizeof(segTree));
    112         for(int i=0; i<n; i++)
    113             scanf("%I64d",&num[i]);
    114         Build(1,1,n);
    115         for (int j=0;j<t;j++)
    116         {
    117             getchar();
    118             scanf("%c%d%d",&C,&x,&y);
    119 
    120             if(C == 'C')
    121             {
    122                 scanf("%d",&z);
    123                 Add(1,x,y,z);
    124             }
    125             else 
    126                 printf("%I64d\n",Query(1,x,y));    
    127         }
    128     
    129     }    
    130     return 0;
    131 }
    
    
  • 相关阅读:
    iOS App之间跳转
    iOS 编码转换
    iOS文件类型判断
    iOS 运行时
    libqrencode生成二维码
    设置app的启动图
    根据字体计算CGRect
    iOS 英文学习
    libev 中IO事件循环解析
    libev 默认事件循环初始化的解析
  • 原文地址:https://www.cnblogs.com/shadervio/p/5696929.html
Copyright © 2011-2022 走看看