线段树入门

线段树,顾名思义就是一棵树,树上每个节点都是一个线段(区间)。

线段树的建树、做单点修改、区间查询都是O(logn)的,区间修改的话如果是单点修改叠加那就是O(nlogn),但是可以用懒惰标记优化。

这些是基础的线段树,进阶部分后面再写了(还没学到).

先上一些线段树基操:

#include<bits/stdc++.h>
using namespace std;
struct node//用结构体数组建树,网上看到好多直接用多个数组的,感觉这样比较简洁明了
{
  int l,r,sum;
  int lazy;
}tree[400];//为防止re,一般开4n大小
int a[100];
inline void update(int k)//更新结点k的sum
{
  tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;
}
void build(int i,int l,int r)//初始化/建树
{
  tree[i].l=l;tree[i].r=r;
  if(l==r)
  {
    tree[i].sum=a[l];//在这里做输入也可以,因为是从左到右建树的
    return;
  }
  int mid=(l+r)>>1;
  build(i*2,l,mid);
  build(i*2+1,mid,r);
  update(i);
}
void pushdown(int k)//将点k的懒惰标记下传
{
  if(tree[k].l==tree[k].r)
  {
    tree[k].lazy=0;
    return;
  }
  tree[k*2].sum+=(tree[k*2].r-tree[k*2+1].l+1)*tree[k].lazy;
  tree[k*2+1].sum+=(tree[k*2+1].r-tree[k*2+1].l+1)*tree[k].lazy;
  tree[k*2].lazy+=tree[k].lazy;
  tree[k*2+1].lazy+=tree[k].lazy;
  tree[k].lazy=0;
}
void change(int k,int x,int var)//单点修改
//当前在k结点,要把x点的值修改为var
{
  if(tree[k].l==tree[k].r){tree[k].sum=var;return;}
  int mid=(tree[k].l+tree[k].r)/2;
  if(x<=mid)change(k*2,x,var);
  else change(k*2+1,x,var);
  update(k);
}
void changeSegment(int k,int l,int r,int x)//区间修改
//当前在k结点,要对区间[l,r]的值+x
{
  if(tree[k].l<=l&&tree[k].r>=r)
  {
    tree[k].sum+=(r-l+1)*x;
    tree[k].lazy+=x;//懒惰标记叠加
    return;
  }
  if(tree[k].lazy)pushdown(k);
  int mid=(tree[k].l+tree[k].r)/2;
  if(l<=mid)changeSegment(k*2,l,mid,x);
  if(r>mid)changeSegment(k*2+1,mid+1,r,x);
  update(k);
}

 

题一:I hate it


很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

Input

本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
Output

对于每一次询问操作,在一行里面输出最高成绩。

Sample Input

5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5

Sample Output

5
6
5
9

这道题涉及了单点修改,区间查询最大值,因为是最大值,差分数组和树状数组就基本没用了,我当时还不会线段树,就用树状数组试了下,直接用单点查询来取代区间查询,T了,所以直接用线段树,对于边做边学线段树的我,这道题搞了半天,有点难受,不过熬过了入门,就好点了。

ac代码:

#include<bits/stdc++.h>
#define N 200010
using namespace std;
int n,m,a[N];
struct node
{
  int l,r,Max;
}t[N<<2];
inline void update(int i)
{
  t[i].Max=max(t[i*2].Max,t[i*2+1].Max);//求的是区间最大值,所以我们要更新的就是最大值
}
void build(int i,int l,int r)//建树
{
  t[i].l=l;t[i].r=r;//我刚开始少了这一行,错漏百出( ̄ ‘i  ̄;)
  if(l==r)
  {
    t[i].Max=a[l];//叶节点的最大值就是它自己
    return;
  }
  int mid=(l+r)/2;
  build(i*2,l,mid);
  build(i*2+1,mid+1,r);
  update(i);//更新
}
int getMax(int i,int l,int r)//查询区间最大值
{
  if(t[i].l>=l&&t[i].r<=r)return t[i].Max;
  int ans=0;
  int mid=(t[i].l+t[i].r)/2;
  if(r<=mid)ans=getMax(i*2,l,r);
  else if(l>mid)ans=getMax(i*2+1,l,r);
  else ans=max(getMax(i*2,l,mid),getMax(i*2+1,mid+1,r));
  return ans;
}
void change(int i,int k,int var)/单点修改
{
  if(t[i].l==t[i].r)
  {
    t[i].Max=var;
    return;
  }
  int mid=(t[i].l+t[i].r)/2;
  if(k<=mid)change(i*2,k,var);
  else change(i*2+1,k,var);
  update(i);
}
int main()
{
  while(scanf("%d%d",&n,&m)!=EOF)
  {
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    build(1,1,n);
    // for(int i=1;i<=n;i++)
    // {
    //   for(int j=i;j<=n;j++)cout<<"getMax("<<i<<","<<j<<")="<<getMax(1,i,j)<<endl;
    // }
    for(int i=1;i<=m;i++)
    {
      char q;
      int A,B;
      scanf(" %c%d%d",&q,&A,&B);
      if(q=='Q')
      {
        printf("%d\n",getMax(1,A,B));
      }
      else change(1,A,B);
    }
  }
  return 0;
}

 

我记得用cinT掉了,害,时间掐的太紧了吧!关闭同步也可以,不过当时不记得怎么关闭同步了,这里再记一下: ios::sync_with_stdio(false)

 

题二:Just a Hook


In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length.



Now Pudge wants to do some operations on the hook.

Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks or golden sticks.
The total value of the hook is calculated as the sum of values of N metallic sticks. More precisely, the value for each kind of stick is calculated as follows:

For each cupreous stick, the value is 1.
For each silver stick, the value is 2.
For each golden stick, the value is 3.

Pudge wants to know the total value of the hook after performing the operations.
You may consider the original hook is made up of cupreous sticks.

Input

The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 10 cases.
For each case, the first line contains an integer N, 1<=N<=100,000, which is the number of the sticks of Pudge’s meat hook and the second line contains an integer Q, 0<=Q<=100,000, which is the number of the operations.
Next Q lines, each line contains three integers X, Y, 1<=X<=Y<=N, Z, 1<=Z<=3, which defines an operation: change the sticks numbered from X to Y into the metal kind Z, where Z=1 represents the cupreous kind, Z=2 represents the silver kind and Z=3 represents the golden kind.
Output

For each case, print a number in a line representing the total value of the hook after the operations. Use the format in the example.
Sample Input

1
10
2
1 5 2
5 9 3

Sample Output

Case 1: The total value of the hook is 24.

这道题是区间修改+区间查询,不过查询都是整个区间的和,本来想着树状数组,不过发现区间修改都是赋值操作,于是又要上线段树,用了单点修改来做区间修改,T了真难受,于是又得学LazyTag懒惰标记,理解了好半天,不过搞懂了就不亏。

ac代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+7;
int n,q,x,y,z;
struct node
{
  int l,r,sum,lazy;
}t[N<<2];
inline void update(int i)
{
  t[i].sum=t[i<<1].sum+t[i<<1|1].sum;
}
void pushdown(int k)
{
  if(t[k].l==t[k].r)
  {
    t[k].lazy=0;
    return;
  }
  t[k<<1].sum=(t[k<<1].r-t[k<<1].l+1)*t[k].lazy;
  t[k<<1|1].sum=(t[k<<1|1].r-t[k<<1|1].l+1)*t[k].lazy;
  t[k<<1].lazy=t[k].lazy;
  t[k<<1|1].lazy=t[k].lazy;
  t[k].lazy=0;
}
void build(int i,int l,int r)
{
  t[i].l=l;t[i].r=r;t[i].lazy=0;
  if(r==l)
  {
    t[i].sum=1;
    return;
  }
  int mid=(l+r)>>1;
  build(i<<1,l,mid);
  build(i<<1|1,mid+1,r);
  update(i);
}
void change(int i,int l,int r,int var)
{
  if(t[i].l>=l&&t[i].r<=r)
  {
    t[i].sum=(t[i].r-t[i].l+1)*var;
    t[i].lazy=var;
    return;
  }
  if(t[i].lazy)pushdown(i);
  int mid=(t[i].l+t[i].r)>>1;
  if(l<=mid)change(i<<1,l,r,var);
  if(r>mid)change(i<<1|1,l,r,var);
  update(i);
}

int main()
{
  int T;
  cin>>T;
  for(int j=1;j<=T;j++)
  {
    scanf("%d%d",&n,&q);
    build(1,1,n);
    for(int i=1;i<=q;i++)
    {
      scanf("%d%d%d",&x,&y,&z);
      change(1,x,y,z);
    }
    cout<<"Case "<<j<<": The total value of the hook is "<<t[1].sum<<"."<<endl;
  }
  return 0;
}

 

结果末尾的"."没看到,真的瞎,白wa了一次,难受。

 

题三:Xenia and Bit Operations


Xenia the beginner programmer has a sequence a, consisting of 2n non-negative integers: a1, a2, ..., a2n. Xenia is currently studying bit operations. To better understand how they work, Xenia decided to calculate some value v for a.

Namely, it takes several iterations to calculate value v. At the first iteration, Xenia writes a new sequence a1 or a2, a3 or a4, ..., a2n - 1 or a2n, consisting of 2n - 1 elements. In other words, she writes down the bit-wise OR of adjacent elements of sequence a. At the second iteration, Xenia writes the bitwise exclusive OR of adjacent elements of the sequence obtained after the first iteration. At the third iteration Xenia writes the bitwise OR of the adjacent elements of the sequence obtained after the second iteration. And so on; the operations of bitwise exclusive OR and bitwise OR alternate. In the end, she obtains a sequence consisting of one element, and that element is v.

Let's consider an example. Suppose that sequence a = (1, 2, 3, 4). Then let's write down all the transformations (1, 2, 3, 4)  →  (1 or 2 = 3, 3 or 4 = 7)  →  (3 xor 7 = 4). The result is v = 4.

You are given Xenia's initial sequence. But to calculate value v for a given sequence would be too easy, so you are given additional m queries. Each query is a pair of integers p, b. Query p, b means that you need to perform the assignment ap = b. After each query, you need to print the new value v for the new sequence a.

Input

The first line contains two integers n and m (1 ≤ n ≤ 17, 1 ≤ m ≤ 105). The next line contains 2n integers a1, a2, ..., a2n (0 ≤ ai < 230). Each of the next m lines contains queries. The i-th line contains integers pi, bi (1 ≤ pi ≤ 2n, 0 ≤ bi < 230) — the i-th query.

Output

Print m integers — the i-th integer denotes value v for sequence a after the i-th query.

Examples

Input
2 4
1 6 3 5
1 4
3 4
1 2
1 2
Output
1
3
3
3

这道题难在读题啊

第一次读题以为是只有最后一次是xor操作,一顿懵以后百度发现是or和xor轮流来整我,所以树的每个节点要多加一个d变量来记录是第几层来对应做or和xor操作,然后把pushup函数改了就过了。这道题只有区间查询,连pushdown都不用了。
ac代码:
#include<bits/stdc++.h>
#define N 1<<17+2
#define lson i<<1
#define rson i<<1|1
#define ll long long
using namespace std;
ll a[N],n,m;
struct node
{
  ll l,r,d,h;
}t[N<<2];
inline void pushup(int i)
{
  t[i].d=t[lson].d+1;
  if(t[i].d%2)t[i].h=t[rson].h|t[lson].h;//令最低层是0层,所以偶数层是or操作,奇数层是xor操作
  else t[i].h=t[rson].h^t[lson].h;
}
void build(int i,int l,int r)
{
  t[i].l=l;
  t[i].r=r;
  if(l==r)
  {
    t[i].h=a[l];
    return;
  }
  int mid=(t[i].r+t[i].l)>>1;
  build(lson,l,mid);
  build(rson,mid+1,r);
  pushup(i);
}
void update(int i,int k,int var)
{
  if(t[i].l==t[i].r)
  {
    t[i].h=var;
    return;
  }
  int mid=(t[i].l+t[i].r)>>1;
  if(k<=mid)update(lson,k,var);
  else update(rson,k,var);
  pushup(i);
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=(1<<n);i++)cin>>a[i];
    build(1,1,1<<n);
    for(int i=1;i<=m;i++)
    {
      ll p,b;
      cin>>p>>b;
      update(1,p,b);
      cout<<t[1].h<<endl;
    }
  return 0;
}

 

题四: Can you answer these queries?

A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of our secret weapon, it could decrease the endurance of a consecutive part of battleships by make their endurance to the square root of it original value of endurance. During the series of attack of our secret weapon, the commander wants to evaluate the effect of the weapon, so he asks you for help.
You are asked to answer the queries that the sum of the endurance of a consecutive part of the battleship line.

Notice that the square root operation should be rounded down to integer.
InputThe input contains several test cases, terminated by EOF.
  For each test case, the first line contains a single integer N, denoting there are N battleships of evil in a line. (1 <= N <= 100000)
  The second line contains N integers Ei, indicating the endurance value of each battleship from the beginning of the line to the end. You can assume that the sum of all endurance value is less than 2 63.
  The next line contains an integer M, denoting the number of actions and queries. (1 <= M <= 100000)
  For the following M lines, each line contains three integers T, X and Y. The T=0 denoting the action of the secret weapon, which will decrease the endurance value of the battleships between the X-th and Y-th battleship, inclusive. The T=1 denoting the query of the commander which ask for the sum of the endurance value of the battleship between X-th and Y-th, inclusive.
OutputFor each test case, print the case number at the first line. Then print one line for each query. And remember follow a blank line after each test case.Sample Input
10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8
Sample Output
Case #1:
19
7
6

这道题是区间修改+区间查询,不过区间修改比较特别,是每个数变成原来的平方根。然后我就觉得线段树没用了, 又看到2s,直接树状数组,T了以后才醒悟n^2怎么可能过,所以用回了线段树,因为是区间开方,所以只能单点修改,O(nlogn)就过了。

ac代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;
ll a[N],n,m;
int cas,k,x,y;
struct node
{
  ll l,r,sum;
  #define l(i) t[i].l
  #define r(i) t[i].r
  #define sum(i) t[i].sum
}t[N<<2];
void pushup(int i)
{
  sum(i)=sum(i<<1)+sum(i<<1|1);
}
void build(int i,int l,int r)
{
  l(i)=l;
  r(i)=r;
  if(l==r)
  {
    sum(i)=a[l];
    return;
  }
  int mid=(l+r)>>1;
  build(i<<1,l,mid);
  build(i<<1|1,mid+1,r);
  pushup(i);
}
void update(int i,int l,int r)
{
  if(l(i)==r(i))
  {
    sum(i)=(ll)sqrt(sum(i));
    return;
  }
  if(l(i)>=l&&r(i)<=r&&sum(i)==r(i)-l(i)+1)return;
  int mid=(r(i)+l(i))>>1;
  if(l<=mid)update(i<<1,l,r);
  if(r>mid)update(i<<1|1,l,r);
  pushup(i);
}
ll getSum(int i,int l,int r)
{
  ll ans=0;
  if(r(i)<=r&&l(i)>=l)return sum(i);
  int mid=(r(i)+l(i))>>1;
  if(r>mid)ans+=getSum(i<<1|1,l,r);
  if(l<=mid)ans+=getSum(i<<1,l,r);
  return ans;
}
int main()
{
  while(scanf("%I64d",&n)!=EOF)
  {
    for(int i=1;i<=n;i++)scanf("%I64d",&a[i]);
    build(1,1,n);
    scanf("%I64d",&m);
    printf("Case #%d:\n",++cas);
    while(m--)
    {
      scanf("%d%d%d",&k,&x,&y);
      if(x>y)swap(x,y);
      if(k==0)
      {
        update(1,x,y);
      }
      else
      {
        printf("%I64d\n",getSum(1,x,y));
      }
    }
printf("\n");
  }
  return 0;
}

 

题五:维护序列

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。

有长为 n 的数列,不妨设为 a1,a2,⋯,an。有如下三种操作形式:

  • 把数列中的一段数全部乘一个值;
  • 把数列中的一段数全部加一个值;
  • 询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模 P 的值。
Input

第一行两个整数 n 和 P

第二行含有 n 个非负整数,从左到右依次为 a1,a2,⋯,an;

第三行有一个整数 M,表示操作总数;

从第四行开始每行描述一个操作,输入的操作有以下三种形式:

  • 操作 1:"1 t g c",表示把所有满足 tig 的 ai 改为 ai×c;
  • 操作 2:"2 t g c",表示把所有满足 tig 的 ai 改为 ai+c;
  • 操作 3:"3 t g",询问所有满足 tig 的 ai 的和模 P 的值。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
Output

对每个操作 3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

Example
Input
7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
Output
2
35
8

好可怕!线段树带着它的乘法来了,而且还混着加法!

还是区间修改+区间查询,这次是1000ms,看了要用lazytag了,又有乘法又有加法怎么搞?机智的我想到了再加一个标记来记录乘法操作不久行了?
很好,每次pushdown就是先乘后加,tag叠加,然后完美wa。
我知道是tag的错误,不过不知道怎么整。最后还是百度解决了!
其实就是tag叠加的错误,因为: (a*x+b)*c+d=a*x*c+b*c+d.
我原先是乘法tag和加法tag都是叠加,根本就错了好吧,乘法tag要连乘,并且要把之前的加法tag也乘上去,加法tag的话就直接加就行了。
ac代码:
 
#include<bits/stdc++.h>
#define ll long long
#define l(i) t[i].l
#define r(i) t[i].r
#define sum(i) t[i].sum
#define tag1(i) t[i].tag1
#define tag2(i) t[i].tag2
#define mo(x) x%=p
using namespace std;
const int N=1e5+10;
int n,m,p,a[N];
struct node
{
  ll l,r,sum,tag1,tag2;
}t[N<<2];
inline void pushup(int i)
{
  sum(i)=(sum(i<<1)+sum(i<<1|1))%p;
}
void pushdown(int i)//要先更新乘法再更新加法。
{
  if(tag1(i)!=1)
  {
    sum(i<<1)*=tag1(i);mo(sum(i<<1));
    sum(i<<1|1)*=tag1(i);mo(sum(i<<1|1));
    tag1(i<<1)*=tag1(i);mo(tag1(i<<1));
    tag1(i<<1|1)*=tag1(i);mo(tag1(i<<1|1));
    tag2(i<<1)*=tag1(i);mo(tag2(i<<1));
    tag2(i<<1|1)*=tag1(i);mo(tag2(i<<1|1));
    tag1(i)=1;
  }
  if(tag2(i))
  {
    sum(i<<1)+=(r(i<<1)-l(i<<1)+1)*tag2(i);mo(sum(i<<1));
    sum(i<<1|1)+=(r(i<<1|1)-l(i<<1|1)+1)*tag2(i);mo(sum(i<<1|1));
    tag2(i<<1)+=tag2(i);mo(tag2(i<<1));
    tag2(i<<1|1)+=tag2(i);mo(tag2(i<<1|1));
    tag2(i)=0;
  }

}
void build(int i,int l,int r)
{
  t[i]={l,r,0,1,0};
  if(l==r)
  {
    scanf("%I64d",&sum(i));
    mo(sum(i));
    return;
  }
  int mid=(l+r)>>1;
  build(i<<1,l,mid);
  build(i<<1|1,mid+1,r);
  pushup(i);
}
void upm(int i,int l,int r,int var)//区间乘法修改
{
  if(r(i)<=r&&l(i)>=l)
  {
    sum(i)*=var;mo(sum(i));
    tag1(i)*=var;mo(tag1(i));
    tag2(i)*=var;mo(tag2(i));
    return;
  }
  int mid=(l(i)+r(i))>>1;
  pushdown(i);
  if(l<=mid)upm(i<<1,l,r,var);
  if(r>mid)upm(i<<1|1,l,r,var);
  pushup(i);
}
void upa(int i,int l,int r,int var)//区间加法修改
{
  if(r(i)<=r&&l(i)>=l)
  {
    sum(i)+=(r(i)-l(i)+1)*var;mo(sum(i));
    tag2(i)=(tag2(i)+var)%p;
    return;
  }
  int mid=(l(i)+r(i))>>1;
  pushdown(i);
  if(l<=mid)upa(i<<1,l,r,var);
  if(r>mid)upa(i<<1|1,l,r,var);
  pushup(i);
}
ll getSum(int i,int l,int r)
{
  if(r(i)<=r&&l(i)>=l)return sum(i)%p;
  ll ans=0;
  pushdown(i);
  int mid=(l(i)+r(i))>>1;
  if(r>mid)ans+=getSum(i<<1|1,l,r)%p;
  if(l<=mid)ans+=getSum(i<<1,l,r)%p;
  return ans%p;
}
int main()
{
  scanf("%d%d",&n,&p);
  build(1,1,n);
  scanf("%d",&m);
  int k,T,g,c;
  while(m--)
  {
    scanf("%d%d%d",&k,&T,&g);
    if(k==1)
    {
      scanf("%d",&c);
      upm(1,T,g,c);
    }
    else if(k==2)
    {
      scanf("%d",&c);
      upa(1,T,g,c);
    }
    else printf("%I64d\n",getSum(1,T,g));
  }
  return 0;
}

 

 题六:BZOJ1756 小白逛公园

小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条"公园路",路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。 一开始,小白就根据公园的风景给每个公园打了分-.-。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a、b两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。 那么,就请你来帮小白选择公园吧。

Input

第一行,两个整数N和M,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。 接下来N行,每行一个整数,依次给出小白 开始时对公园的打分。 接下来M行,每行三个整数。第一个整数K,1或2。K=1表示,小新要带小白出去玩,接下来的两个整数a和b给出了选择公园的范围(1≤a,b≤N);K=2表示,小白改变了对某个公园的打分,接下来的两个整数p和s,表示小白对第p个公园的打分变成了s(1≤p≤N)。 其中,1≤N≤500 000,1≤M≤100 000,所有打分都是绝对值不超过1000的整数。

Output

小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。

Example
Input
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 2 3
Output
2
-1

用线段树没毛病,可是要记录区间最大连续和怎么搞?总不能暴力吧?
想了半天,好吧我认输,百度:https://www.cnblogs.com/JHTBlog/p/11251157.html
这篇博客写得太好了,我看了一眼马上就回了,后悔之前没有再认真思考一下,因为真的不难想出来!以后还真要多多认真思考!
直接贴代码了:
 
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e5+2;
int n,m;
struct node
{
  int l,r;
  int sum,rsum,lsum,asum;//我这里是sum为区间和,asum是最大连续区间和
  #define l(i) t[i].l
  #define r(i) t[i].r
  #define sum(i) t[i].sum
  #define lsum(i) t[i].lsum
  #define rsum(i) t[i].rsum
  #define asum(i) t[i].asum
  #define lson i<<1
  #define rson i<<1|1
}t[N<<2];
inline void pushup(int i)
{
  sum(i)=sum(lson)+sum(rson);
  lsum(i)=max(lsum(lson),sum(lson)+lsum(rson));
  rsum(i)=max(rsum(rson),sum(rson)+rsum(lson));
  asum(i)=max(asum(rson),max(lsum(rson)+rsum(lson),asum(lson)));
}
void build(int i,int l,int r)
{
  l(i)=l;r(i)=r;
  if(l==r)
  {
    scanf("%d",&sum(i));
    lsum(i)=rsum(i)=asum(i)=sum(i);
    return ;
  }
  int mid=(l+r)>>1;
  build(lson,l,mid);
  build(rson,mid+1,r);
  pushup(i);
}
void update(int i,int k,int var)
{
  if(l(i)==r(i))
  {
    sum(i)=var;
    lsum(i)=var;
    rsum(i)=var;
    asum(i)=var;
    return;
  }
  int mid=(l(i)+r(i))>>1;
  if(k>mid)update(rson,k,var);
  else update(lson,k,var);
  pushup(i);
}

node query(int i,int l,int r)
{
  if(l(i)>=l&&r(i)<=r)return t[i];
  bool rl=0,rr=0;
  node a,b;
  int mid=(l(i)+r(i))>>1;
  if(l<=mid)
  {
    rl=1;a=query(lson,l,r);
  }
  if(r>mid)
  {
    rr=1;b=query(rson,l,r);
  }
  node ans;
  if(rl==1&&rr==0)ans=a;
  if(rl==0&&rr==1)ans=b;
  if(rl==1&&rr==1)
  {
    ans.sum=a.sum+b.sum;
    ans.lsum=max(a.lsum,a.sum+b.lsum);
    ans.rsum=max(b.rsum,b.sum+a.rsum);
    ans.asum=max(a.asum,max(b.asum,a.rsum+b.lsum));
  }
  return ans;
}
int main()
{
  scanf("%d%d",&n,&m);
  build(1,1,n);
  int k,x,y;
  while(m--)
  {
    scanf("%d%d%d",&k,&x,&y);
    if(k==1)
    {
      if(x>y)swap(x,y);//这里我被坑到了,以后碰到区间题要自觉加上
      printf("%d\n",query(1,x,y).asum);
    }
    if(k==2)update(1,x,y);
  }
  return 0;
}

 

 瞎说:

一个线段树都学了好些天,还学的是入门级操作,还有好多优化还没去学。

前两天跟一个大佬抱怨线段树好烦,他反手就打我脸:线段树算是很简单的数据结构了。我:ε(┬┬﹏┬┬)3

毕竟是个acm新人,没什么基础,有点难是正常的吧,不过我相信我可以学好的,继续坚持!

 
 
 

 

posted @ 2020-02-14 22:30  Hai_Lin  阅读(179)  评论(0)    收藏  举报