虽然训练计划里这两道题目不是归为线段树的,但是觉得这几道题目都是可以用线段树解决的。
题目:http://poj.org/problem?id=3468
题意:给你N个数,下面是两种操作 C a b c是说把从a 到 b 的数全部加上 c ,Q a b 是说询问 从a 到 b的和
思路:线段树的成段更新,线段树中节点除了区间端点外和保存区间和以外,加一个记录权重的,也就是当一段被加上一个数时,这段的权重进行修改,根据权重和原先记录的和求得修改以后的和
View Code
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define N 100010 5 6 using namespace std; 7 8 typedef long long ll; 9 struct node 10 { 11 int l, r; 12 ll s, d; // d 保存每段修改的数 c ,s 用来记录区间和 13 }tr[N * 3]; 14 int val[N]; 15 void build(int s,int e,int d) 16 { 17 tr[d].l = s; 18 tr[d].r = e; 19 tr[d].d = 0; 20 if( s == e) 21 { 22 tr[d].s = val[s]; 23 return ; 24 } 25 int mid = (s + e) / 2; 26 build(s,mid,d * 2); 27 build(mid + 1,e,d * 2 + 1); 28 tr[d].s = tr[d * 2].s + tr[d * 2 + 1].s; 29 } 30 void insert(int s,int e,int d,int v) 31 { 32 if(s == tr[d].l && e == tr[d].r) 33 { 34 tr[d].d += v; // 更新权重 35 return ; 36 } 37 int mid = (tr[d].l + tr[d].r) / 2; 38 if(e <= mid) insert(s,e,d * 2,v); 39 else if(s > mid) insert(s,e,d * 2 + 1,v); 40 else 41 { 42 insert(s,mid,d * 2,v); 43 insert(mid + 1,e,d * 2 + 1,v); 44 } 45 // 计算修改的值 46 int ans1 = tr[d * 2].d * (tr[d * 2].r - tr[d * 2].l + 1); 47 int ans2 = tr[d * 2 + 1].d * (tr[d * 2 + 1].r - tr[d * 2 + 1].l + 1); 48 int ans3 = tr[d * 2].s + tr[d * 2 + 1].s; 49 tr[d].s = ans1 + ans2 + ans3; 50 } 51 ll quey(int s,int e,int d) 52 { 53 if(tr[d].l == s && tr[d].r == e) 54 { 55 return tr[d].s + tr[d].d * (e - s + 1); 56 } 57 ll sum; 58 int mid = (tr[d].l + tr[d].r) / 2; 59 if(e <= mid) sum = quey(s,e,d * 2); 60 else if(s > mid) sum = quey(s,e,d * 2 + 1); 61 else sum = quey(s,mid,d * 2) + quey(mid + 1,e,d * 2 + 1); 62 return sum + tr[d].d * (e - s + 1); 63 } 64 int main() 65 { 66 int i,n,m; 67 int x,y,z; 68 char str[2]; 69 freopen("data.txt","r",stdin); 70 while(scanf("%d%d", &n, &m) != EOF) 71 { 72 for(i = 1; i <= n; i++) scanf("%d", &val[i]); 73 build(1,n,1); 74 while(m--) 75 { 76 scanf("%s",str); 77 if(str[0] == 'C') 78 { 79 scanf("%d%d%d",&x,&y,&z); 80 insert(x,y,1,z); 81 } 82 else 83 { 84 scanf("%d%d",&x,&y); 85 printf("%lld\n", quey(x,y,1)); 86 } 87 } 88 } 89 return 0; 90 }
题目:http://poj.org/problem?id=2892
题意:首先给出N个联通的城堡,D x 表示第 x 个城堡被摧毁,Q x 表示询问包括 x 在内的城堡最长连续没被摧毁的城堡个数,R表示修复最后一个被摧毁的城堡(如果有连续的 R 然后是倒数第二个,倒数第三个 ~~~~)
这个题目数据可能弱了,用暴力也能过,就是开两个数组,一个记录城堡的好坏,一个记录城堡被摧毁的顺序
View Code
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <algorithm> 5 #define N 50010 6 #define _clr(a,val) (memset(a,val,sizeof(a))) 7 8 using namespace std; 9 10 int stack[N]; 11 int good[N]; 12 int main() 13 { 14 int i,x; 15 int n,m; 16 char str[2]; 17 //freopen("data.txt","r",stdin); 18 while(scanf("%d%d",&n,&m) != EOF) 19 { 20 _clr(stack,0); 21 _clr(good,0); 22 int num = 0; 23 while(m--) 24 { 25 scanf("%s",str); 26 if(str[0] == 'D') 27 { 28 scanf("%d",&x); 29 good[x] = 1; 30 stack[num++] = x; 31 } 32 else if(str[0] == 'R') 33 { 34 scanf("%d",&x); 35 good[stack[--num]] = 0; 36 } 37 else 38 { 39 int ans = 1; 40 scanf("%d",&x); 41 if(good[x] == 1) 42 { 43 printf("0\n"); 44 continue; 45 } 46 for(i = x + 1; i <= n; i++) 47 { 48 if(good[i] == 0) ans++; 49 else break; 50 } 51 for(i = x - 1; i >= 1; i--) 52 { 53 if(good[i] == 0) ans++; 54 else break; 55 } 56 printf("%d\n",ans); 57 } 58 } 59 } 60 return 0; 61 }
还有就是用线段树,具体注释看代码吧
View Code
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <algorithm> 5 #define N 50010 6 #define _clr(a,val) (memset(a,val,sizeof(a))) 7 8 using namespace std; 9 struct node 10 { 11 int lmax; // 保存该区间从左端点起最长连续的城堡数(没有被摧毁的) 12 int rmax; // 保存该区间到右端点结束的最长连续城堡数 13 int val; // 记录城堡是否被摧毁,1代表被摧毁,0代表完好 14 }tr[N * 4]; 15 int stack[N]; 16 void build(int s,int e,int d) 17 { 18 tr[d].lmax = tr[d].rmax = e - s + 1; 19 if(s == e) return; 20 int mid = (s + e) / 2; 21 build(s,mid,d * 2); 22 build(mid + 1,e,d * 2 + 1); 23 } 24 void insert(int s,int e,int d,int x,int val) 25 { 26 if(s == e) 27 { 28 tr[d].val = val; 29 if(val) tr[d].lmax = tr[d].rmax = 0; 30 else tr[d].lmax = tr[d].rmax = 1; 31 return ; 32 } 33 int mid = (s + e) / 2; 34 int l = d * 2; 35 int r = d * 2 + 1; 36 if(x <= mid) insert(s,mid,l,x,val); 37 else insert(mid + 1,e,r,x,val); 38 if(tr[l].lmax == mid - s + 1) tr[d].lmax = tr[r].lmax + tr[l].rmax; // 如果左孩子中没有被摧毁的城堡,那么当前区间的 lmax 就是 左 + 右 39 else tr[d].lmax = tr[l].lmax; // 否则就直接等于 左 40 if(tr[r].rmax == e - mid) tr[d].rmax = tr[r].rmax + tr[l].rmax; // 该区间的 rmax 计算如上 41 else tr[d].rmax = tr[r].rmax; 42 } 43 int quey(int s,int e,int d,int x) 44 { 45 if(e == s) 46 { 47 if(tr[d].val == 0) return 1; 48 else return 0; 49 } 50 int mid = (s + e) / 2; 51 int l = d * 2; 52 int r = d * 2 + 1; 53 if(x <= mid) // 询问点在左孩子的情况 54 { 55 if(x > mid - tr[l].rmax) return tr[l].rmax + tr[r].lmax; // 如果询问的点在 以左孩子右端点为终点的最长连续城堡 的范围内,那么所求答案就是左孩子的 rmax + 右孩子的 lmax 56 else return quey(s,mid,l,x); // 如果不在上诉范围内,那么就递归访问左孩子,继续查找 57 } 58 else // 询问点在右孩子的情况 计算方法同左孩子 59 { 60 if(x <= mid + tr[r].lmax) return tr[l].rmax + tr[r].lmax; 61 else return quey(mid + 1,e,r,x); 62 } 63 } 64 int main() 65 { 66 int x; 67 int n,m; 68 char str[2]; 69 //freopen("data.txt","r",stdin); 70 while(scanf("%d%d",&n,&m) != EOF) 71 { 72 _clr(stack,0); 73 build(1,n,1); 74 int num = 0; 75 while(m--) 76 { 77 scanf("%s",str); 78 if(str[0] == 'D') 79 { 80 scanf("%d",&x); 81 stack[num ++] = x; 82 insert(1,n,1,x,1); 83 } 84 else if(str[0] == 'R') 85 { 86 insert(1,n,1,stack[-- num],0); 87 } 88 else 89 { 90 scanf("%d",&x); 91 printf("%d\n",quey(1,n,1,x)); 92 } 93 } 94 } 95 return 0; 96 }