线段树之区间更新
第三篇终于来啦~虽然知道我更新的很慢,但每一篇我都会努力去写的,这是很久之前做的题,想着博客也该写一写了,于是带着一半复习一半更新博客的想法写的,希望各位能喜欢。
Question:
Description:
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.
Answer:
数据预处理:
这个题只需要数据范围的指定
#define MAXN 100010
自定义数据类型:
这个仍旧用结构体数组实现线段树,结构体里包含这个节点所维护的区间[left,right],区间内的总价值,以及延迟标记delay
struct Tree
{
int left,right;
int val,delay;//val是当前区间的总价值,delay延迟标记
}tr[MAXN*4];
main()函数的实现:
int main()
{
int T;
scanf("%d",&T);
for(int t=0;t<T;t++)//T组测试数据
{
int n;
scanf("%d",&n);//区间长度
build(1,1,n);//初始化线段树
int q;
scanf("%d",&q);//操作次数
while(q--)
{
int l,r,lv;
scanf("%d%d%d",&l,&r,&lv);
updata(1,l,r,lv);//树的更新
}
printf("Case %d: The total value of the hook is %d.\n",t+1,tr[1].val);
}
return 0;
}
子函数的实现:
第一个是线段树初始化函数:
//build()用于建树,无返回值,参数为根节点(id)和节点保存到区间[l,r]
void build(int id,int l,int r)
{
tr[id].left=l;
tr[id].right=r;
tr[id].delay=0;
if(l==r)
tr[id].val=r-l+1;//因为材料是铜,价值为一,所以叶子节点的val值初始化为区间长度
else
{
int mid=(l+r)/2;
build(id*2,l,mid);//递归建立左子树
build(id*2+1,mid+1,r);//递归建立右子树
tr[id].val=tr[id*2].val+tr[id*2+1].val;//维护每个区间val
}
}
第二个是数据更新的函数,用于维护线段树:
//updata()用于数据更新,无返回值,参数为当前更新的节点(id),需要被维护的区间[l,r],维护的值,这里是改变材料,也就是改变价值
void updata(int id,int l,int r,int lv)
{
if(l<=tr[id].left&&r>=tr[id].right)//这个区间在要更新的范围内
{
tr[id].delay=lv;//延迟标记,表明这个节点的子树需要被更新
tr[id].val=lv*(tr[id].right-tr[id].left+1);//更新当前节点的val,它等于材料的价值*区间长度
}
else//搜寻需要被维护的节点
{
if(tr[id].delay!=0)//这个节点的子树需要被更新,然而还没更新
{
pushdown(id);//更新这个节点的子树
tr[id].delay=0;//记得把标记取消掉
}
int mid=(tr[id].left+tr[id].right)/2;
if(r<=mid)
{
updata(id*2,l,r,lv);//搜寻左子树
pushup(id);//回溯维护这个节点val
}
else if(l>mid)
{
updata(id*2+1,l,r,lv);//搜寻右子树
pushup(id);//回溯维护这个节点的val
}
else
{
updata(id*2,l,r,lv);
updata(id*2+1,l,r,lv);
pushup(id);
}
}
}
下一个是向上维护线段树:
//pushup()用于维护当前节点的val,无返回值,参数是当前节点(id),用左右子树的更新当前节点
void pushup(int id)
{
tr[id].val=tr[id*2].val+tr[id*2+1].val;
}
最后是向下维护线段树:
//用于更新当前节点的左右子树,并把标记传到左右子树,无返回值,参数为当前节点(id)
void pushdown(int id)
{
tr[id*2].delay=tr[id].delay;
tr[id*2+1].delay=tr[id].delay;
tr[id*2].val=tr[id].delay*(tr[id*2].right-tr[id*2].left+1);
tr[id*2+1].val=tr[id].delay*(tr[id*2+1].right-tr[id*2+1].left+1);
}
完成!看来上一篇博客是不是觉得这个题很简单呢?所用的思想都是一样的,通过延迟标记来降低时间复杂度,更新什么的都是套路,只要掌握了其本质什么样的更新都是一样的。
时空复杂度就像这样:
Memory | Time | Length |
---|---|---|
5532(Kb) | 967(Ms) | 1580(Bytes) |
第三篇就到这里啦(^ω^)!谢谢各位!下次再见啦!