线段树

线段树 详解请看大佬博客:http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html

几个模板题:

 1.洛谷 P3372 【模板】线段树 1

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入输出格式

输入格式:

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出格式:

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入样例#1:
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出样例#1:
11
8
20

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

(数据已经过加强^_^,保证在int64/long long数据范围内)

样例说明:

思路:

  纯模板题,数据范围有提示在long long范围内,注意ans一定要清0!!!

  cin全都MLE,改成scanf就欧了。

(⊙v⊙)嗯~ 代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 
 5 typedef long long LL;
 6 const LL N = 1e9;
 7 LL n,m,a,b,c,d,ans;
 8 
 9 struct Tree{
10     LL x,y,w,f;
11 }tree[400001];
12 
13 void build(LL k,LL l,LL r) {
14     tree[k].x = l,tree[k].y = r;
15     if(l == r) {
16 //        cin>>tree[k].w;
17         scanf("%lld",&tree[k].w);
18         return ;
19     }
20     LL mid = (l + r)/2;
21     build(k*2,l,mid);
22     build(k*2+1,mid+1,r);
23     tree[k].w=tree[k*2].w + tree[k*2+1].w;
24 }
25 
26 void down(int k) {
27     tree[k*2].f+=tree[k].f;
28     tree[k*2+1].f+=tree[k].f;
29     tree[k*2].w+=tree[k].f * (tree[k*2].y - tree[k*2].x + 1);
30     tree[k*2+1].w+=(tree[k*2+1].y - tree[k*2+1].x +1) * tree[k].f;
31     tree[k].f = 0;
32 }
33 
34 void Change(int k) {
35     if(tree[k].x>=a&&tree[k].y<=b){
36         tree[k].w+=(tree[k].y-tree[k].x+1)*d;
37         tree[k].f+=d;
38         return ;
39     }
40     if(tree[k].f) down(k);
41     int mid = (tree[k].x + tree[k].y)/2;
42     if(a<=mid) Change(k*2);
43     if(b>mid) Change(k*2+1);
44     tree[k].w = tree[k*2].w + tree[k*2+1].w;
45 }
46 
47 void Ask(int k) {
48     if(tree[k].x>=a && tree[k].y<=b) {
49         ans += tree[k].w ;
50         return ;
51     }
52     if(tree[k].f) down(k);
53     int mid = (tree[k].x + tree[k].y)/2;
54     if(a<=mid) Ask(k*2);
55     if(b>mid) Ask(k*2+1);
56 }
57 
58 int main() {
59     cin>>n>>m;
60     build(1,1,n);
61     for(LL i=1; i<=m; i++) {
62 //        cin>>c;
63         scanf("%lld",&c);
64         if(c == 1) {
65 //            cin>>a>>b>>d;
66             scanf("%lld%lld%lld",&a,&b,&d);
67             Change(1);
68         }
69         if(c == 2) {
70 //            cin>>a>>b;
71             scanf("%lld%lld",&a,&b);
72             ans=0,Ask(1);
73 //            cout<<ans<<endl;
74             printf("%lld
",ans);
75         }
76     }
77     return 0;
78 } 

2.Codevs 1080 线段树练习

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 钻石 Diamond
题目描述 Description

一行N个方格,开始每个格子里都有一个整数。现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中所有元素的和;修改的规则是指定某一个格子x,加上或者减去一个特定的值A。现在要求你能对每个提问作出正确的回答。1≤N<100000,,提问和修改的总数m<10000条。

输入描述 Input Description

输入文件第一行为一个整数N,接下来是n行n个整数,表示格子中原来的整数。接下一个正整数m,再接下来有m行,表示m个询问,第一个整数表示询问代号,询问代号1表示增加,后面的两个数x和A表示给位置X上的数值增加A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求[a,b]之间的区间和。

输出描述 Output Description

共m行,每个整数

样例输入 Sample Input

6

3

4

1 3 5

2 1 4

1 1 9

2 2 6

样例输出 Sample Output

22

22

数据范围及提示 Data Size & Hint

1≤N≤100000, m≤10000 。

分类标签 Tags 点此展开 

思路:
  纯模板题。。。
(⊙v⊙)嗯~ 代码:
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 
 5 typedef long long LL;
 6 const int N = 400001;
 7 int n,m,p;
 8 struct Tree{
 9     int l,r,w,f;
10 }tree[N];
11 LL a,b,ch,ans,typ;
12 
13 void build(int k,int ll,int rr){
14     tree[k].l = ll,tree[k].r = rr;
15     if(tree[k].l==tree[k].r) {
16         scanf("%lld",&tree[k].w);
17         return ;
18     }
19     int mid=(ll+rr)/2;
20     build(k*2,ll,mid);
21     build(k*2+1,mid+1,rr);
22     tree[k].w = tree[k*2].w + tree[k*2+1].w ;
23 }
24 
25 void down(int k) { //懒标记 
26     tree[k*2].f += tree[k].f ;
27     tree[k*2+1].f += tree[k].f ;
28     tree[k*2].w += tree[k].f * (tree[k*2].l - tree[k*2].r + 1);
29     tree[k*2+1].w += tree[k].f * (tree[k*2+1].l -tree[k*2+1].r +1);
30     tree[k].f = 0;
31 }
32 
33 void Ask(int k){//区间询问 
34     if(tree[k].l>=a&&tree[k].r<=b){
35         ans+=tree[k].w;
36         return ;
37     }
38     if(tree[k].f) down(k);
39     int mid = (tree[k].r + tree[k].l)/2;
40     if(a<=mid) Ask(k*2);
41     if(b>mid) Ask(k*2+1);
42 }
43 
44 void Change_only(int k){
45     if(tree[k].l==tree[k].r) {
46         tree[k].w += ch;
47         return ;
48     }
49     int mid = (tree[k].r + tree[k].l)/2;
50     if(a<=mid) Change_only(k*2);
51     else Change_only(k*2+1);
52     tree[k].w = tree[k*2].w + tree[k*2+1].w ;
53 }
54 
55 int main() {
56     cin>>n;
57     build(1,1,n);
58     cin>>m;
59     while(m--){
60         cin>>typ;
61         if(typ==1) {
62             scanf("%lld%lld",&a,&ch);
63             Change_only(1);
64         }
65         if(typ==2){
66             scanf("%lld%lld",&a,&b);
67             ans=0;
68             Ask(1);
69             cout<<ans<<endl;
70         }
71     }
72     return 0;
73 }

3.Codevs 1081 线段树练习 2

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 大师 Master
题目描述 Description

给你N个数,有两种操作

1:给区间[a,b]的所有数都增加X

2:询问第i个数是什么?

输入描述 Input Description

第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数。如果第一个数是1,后接3个正整数a,b,X,表示在区间[a,b]内每个数增加X,如果是2,后面跟1个整数i, 表示询问第i个位置的数是多少。

输出描述 Output Description

对于每个询问输出一行一个答案

样例输入 Sample Input

3

1

2

3

2

1 2 3 2

2 3

样例输出 Sample Output

5

数据范围及提示 Data Size & Hint

数据范围

1<=n<=100000

1<=q<=100000

分类标签 Tags 点此展开 

思路:
  纯模板题。。。
(⊙v⊙)嗯~ 代码:
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 
 5 typedef long long LL;
 6 const int N = 400001;
 7 int n,m,p;
 8 struct Tree{
 9     int l,r,w,f;
10 }tree[N];
11 LL a,b,ch,ans,typ;
12 
13 void build(int k,int ll,int rr){
14     tree[k].l = ll,tree[k].r = rr;
15     if(tree[k].l==tree[k].r) {
16         scanf("%lld",&tree[k].w);
17         return ;
18     }
19     int mid=(ll+rr)/2;
20     build(k*2,ll,mid);
21     build(k*2+1,mid+1,rr);
22     tree[k].w = tree[k*2].w + tree[k*2+1].w ;
23 }
24 
25 void down(int k) { //懒标记 
26     tree[k*2].f += tree[k].f ;
27     tree[k*2+1].f += tree[k].f ;
28     tree[k*2].w += tree[k].f * (tree[k*2].l - tree[k*2].r + 1);
29     tree[k*2+1].w += tree[k].f * (tree[k*2+1].l -tree[k*2+1].r +1);
30     tree[k].f = 0;
31 }
32 
33 void Change(int k){//区间修改 
34     if(tree[k].l>=a&&tree[k].r<=b) {
35         tree[k].w += (tree[k].r - tree[k].l + 1) * ch;
36         tree[k].f += ch;
37         return ;
38     }
39     if(tree[k].f) down(k);
40     int mid = (tree[k].r + tree[k].l)/2;
41     if(a<=mid) Change(k*2);
42     if(b>mid) Change(k*2+1);
43     tree[k].w = tree[k*2].w + tree[k*2+1].w ;
44 }
45 
46 void Ask_only(int k) {
47     if(tree[k].l==tree[k].r){
48         ans=tree[k].w;
49         return ;
50     }
51     if(tree[k].f) down(k);
52     int mid = (tree[k].r + tree[k].l)/2;
53     if(a<=mid) Ask_only(k*2);
54     else Ask_only(k*2+1);
55 }
56 
57 int main() {
58     cin>>n;
59     build(1,1,n);
60     cin>>m;
61     while(m--){
62         cin>>typ;
63         if(typ==1) {
64             scanf("%lld%lld%lld",&a,&b,&ch);
65             Change(1);
66         }
67         if(typ==2){
68             scanf("%lld",&a);
69             Ask_only(1);
70             cout<<ans<<endl;
71         }
72     }
73     return 0;
74 }

4.Codevs 1082 线段树练习 3

 时间限制: 3 s
 空间限制: 128000 KB
 题目等级 : 大师 Master
题目描述 Description

给你N个数,有两种操作:

1:给区间[a,b]的所有数增加X

2:询问区间[a,b]的数的和。

输入描述 Input Description

第一行一个正整数n,接下来n行n个整数,

再接下来一个正整数Q,每行表示操作的个数,

如果第一个数是1,后接3个正整数,

表示在区间[a,b]内每个数增加X,如果是2,

表示操作2询问区间[a,b]的和是多少。

pascal选手请不要使用readln读入

输出描述 Output Description

对于每个询问输出一行一个答案

样例输入 Sample Input

3

1

2

3

2

1 2 3 2

2 2 3

样例输出 Sample Output

9

数据范围及提示 Data Size & Hint

数据范围

1<=n<=200000

1<=q<=200000

分类标签 Tags 点此展开 

思路:
  纯模板题。。。
(⊙v⊙)嗯~ 代码:
 1 #include<cstdio>
 2 using namespace std;
 3 
 4 const int N = 900001;
 5 int n,m,a,b,ch,typ;;
 6 struct Tree{
 7     long long l,r,w,f;
 8 }tree[N];
 9 long long ans;
10 
11 void build(int k,int ll,int rr){
12     tree[k].l = ll,tree[k].r = rr;
13     if(tree[k].l==tree[k].r) {
14         scanf("%d",&tree[k].w);
15         return ;
16     }
17     int mid=(ll+rr)/2;
18     build(k*2,ll,mid);
19     build(k*2+1,mid+1,rr);
20     tree[k].w = tree[k*2].w + tree[k*2+1].w ;
21 }
22 
23 void down(int k) { //懒标记 
24     tree[k*2].f += tree[k].f ;
25     tree[k*2+1].f += tree[k].f ;
26     tree[k*2].w += tree[k].f * (tree[k*2].r - tree[k*2].l + 1);
27     tree[k*2+1].w += tree[k].f * (tree[k*2+1].r -tree[k*2+1].l +1);
28     tree[k].f = 0;
29 }
30 
31 void Ask(int k){//区间询问 
32     if(tree[k].l>=a&&tree[k].r<=b){
33         ans+=tree[k].w;
34         return ;
35     }
36     if(tree[k].f) down(k);
37     int mid = (tree[k].r + tree[k].l)/2;
38     if(a<=mid) Ask(k*2);
39     if(b>mid) Ask(k*2+1);
40 }
41 
42 void Change(int k){//区间修改 
43     if(tree[k].l>=a&&tree[k].r<=b) {
44         tree[k].w += (tree[k].r - tree[k].l + 1) * ch;
45         tree[k].f += ch;
46         return ;
47     }
48     if(tree[k].f) down(k);
49     int mid = (tree[k].r + tree[k].l)/2;
50     if(a<=mid) Change(k*2);
51     if(b>mid) Change(k*2+1);
52     tree[k].w = tree[k*2].w + tree[k*2+1].w ;
53 }
54 
55 int main() {
56     scanf("%d",&n);
57     build(1,1,n);
58     scanf("%d",&m);
59     while(m--){
60         scanf("%d",&typ);
61         ans=0;
62         if(typ==1) {
63             scanf("%d%d%d",&a,&b,&ch);
64             Change(1);
65         }
66         if(typ==2){
67             scanf("%d%d",&a,&b);
68             Ask(1);
69             printf("%lld
",ans);
70         }
71     }
72     return 0;
73 }

自己选的路,跪着也要走完!!!

原文地址:https://www.cnblogs.com/wsdestdq/p/6832960.html