1 10 2 1 5 2 5 9 3
Case 1: The total value of the hook is 24.
分析:看到题目首先想到了线段树,(因为题目是区间更新)区间更新需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候。延迟标记的意思是:这个区间的左右儿子都需要被更新,但是当前区间已经更新了
首先 区间 [1,n] 建立线段树,(题目例子 n==10) 如下图;
然后依次更新区间,分3中情况,向左,向右,分开向左向右,
在修改和查询的时候,如果我们到了一个结点p,并且需要分开决定考虑其子结点(不能完全覆盖),那么我们就要看看结点p有没有标记,如果有,就要按照标记修改其子结点的信息,并且给子结点都标上相同的标记,同时消掉p的标记;
进行更新操作
1 5 2
区间更新后的树如下:
进行更新操作
5 9 3
区间更新后的树如下
假如 再 进行更新操作
6 10 1
区间更新后的树如下
#define N 100001 #include <iostream> #include <stdio.h> #include <algorithm> using namespace std; typedef struct TREE { int lside,rside,value; }TREE; TREE tree[N*4];//线段树占空间约为结点个数的 4 倍 int sum,temp; void build_tree(int l,int r,int i) { tree[i].lside = l; tree[i].rside = r; tree[i].value = 1;//cuperous if(tree[i].lside == tree[i].rside) return; int mid = (tree[i].lside + tree[i].rside)>>1; build_tree(l,mid,i<<1); //i*2 build_tree(mid+1,r,i<<1|1); //i*2+1 } void change(int x,int y,int z,int i) { // printf("x=%d,y=%d,i=%d,tl=%d,tr=%d,v=%d\n",x,y,i,tree[i].lside,tree[i].rside,tree[i].value); if(x<= tree[i].lside && y>=tree[i].rside)//正好与跟新区间相同 { tree[i].value = z; return ; } if(tree[i].value>0)//不能完全覆盖,需要将下面的都左右子树 都先覆盖为当前结点的标志, { tree[i<<1].value = tree[i].value; tree[i<<1|1].value = tree[i].value; tree[i].value = 0; } int mid = (tree[i].lside + tree[i].rside)>>1; if(y<=mid) change(x,y,z,i<<1);//向左 else if( x>mid ) change(x,y,z,i<<1|1 );//向右 else//分开的 { change(x,mid,z,i<<1); change(mid+1,y,z,i<<1|1); } } void count(int i) { if(tree[i].value>0) { sum += tree[i].value * (tree[i].rside - tree[i].lside + 1);//区间的value * 区间的长度 return; } count(i<<1); count(i<<1|1); } int main() { int t,i,j,n,q,x,y,z; scanf("%d",&t); for(i=1;i<=t;i++) { scanf("%d",&n); build_tree(1,n,1); scanf("%d",&q); while(q--) { scanf("%d%d%d",&x,&y,&z); change(x,y,z,1); // for(j=1;j<40;j++) // printf("j=%d,l=%d,r=%d,v=%d\n",j,tree[j].lside,tree[j].rside,tree[j].value); } sum = 0; count(1); printf("Case %d: The total value of the hook is %d.\n",i,sum); } return 0; } /* 7 10 3 1 5 2 5 9 3 6 10 1 */