D.温暖的签到题(线段树 区间修改)

温暖的签到题

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld

题目描述

给你一个长度为n的序列,初始为1,2,3…n,对其进行m次操作。
操作有两种:
1 l r 表示将区间[l,r]用 [1,2…r-l+1] 覆盖
2 l r 查询[l,r]的区间和

输入描述:
第一行包含2个数字,n,m(1 <= n,m <= 1e5)

接下来包含m行,格式如题面所示
输出描述:
对于每个操作2,输出一行一个整数表示答案

输入

10 5
2 1 10
1 3 6
2 1 10
1 1 10
2 1 10
输出

55
47
55
首先可以看出是个线段树的题,然后需要考虑如何修改区间的值,可以用懒惰标记记录当前需要修改的区间的总和,然后记录当前区间左节点应该加上多少,构造一个函数即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 300010;
typedef long long ll;
ll arr[N];
struct node{
	ll val;//具体数值
	//ll len;//覆盖区间长度
	ll lazy;//延迟标记
	ll l, r;//左右儿子编号
	void modify(ll p){
		val = (p + p + (r - l))*(r - l + 1) / 2;//区间内应该加的和
		lazy = p;//标记
	}
}tree[N];
//建树
void buildtree(ll root, ll left, ll right){
	tree[root].lazy = 0;
	tree[root].l = left;//记录每个节点包含的左右端点
	tree[root].r = right;
	//tree[root].len = right - left + 1;//每个区间的长度
	if (left == right){//到达根节点
		tree[root].val = arr[left];//价值即为叶节点的权值
		return;
	}
	buildtree(root << 1, left, (left + right) / 2);//建左儿子树
	buildtree((root << 1) | 1, (left + right) / 2 + 1, right);//建右儿子树
	tree[root].val = tree[root << 1].val + tree[(root << 1) | 1].val;//权值求和
}
//单点更新
void add(ll root, ll id, ll addval){//根节点,待更新的点在原数组arr的下标,更新的值
	if (tree[root].l == tree[root].r){
		tree[root].val += addval;
		return;
	}
	ll mid = (tree[root].l + tree[root].r) >> 1;
	if (id <= mid) add(root << 1, id, addval);//判断在左还是在右
	else add((root << 1) | 1, id, addval);
	tree[root].val = tree[root << 1].val + tree[(root << 1) | 1].val;//为左右儿子之和
}
//核心操作,懒惰标记
void pushdown(ll root){//向下传递lazy标记
	if (tree[root].lazy){
		ll l = tree[root].l, r = tree[root].r, m = l + r >> 1;
		tree[root << 1].modify(tree[root].lazy);
		tree[root << 1|1].modify(tree[root].lazy+m-l+1);
		tree[root].lazy = 0;
	}
}
//区间更新
void update(ll root, ll left, ll right){
	if (tree[root].l >= left&&tree[root].r <= right){
		tree[root].modify(tree[root].l - left + 1);
		//当前区间修改的第一个值为几
		return;
	}
	if (tree[root].l > right || tree[root].r < left)
		return;
	if (tree[root].lazy) pushdown(root);
	update(root << 1, left, right);
	update((root << 1) | 1, left, right);
	tree[root].val = tree[root << 1].val + tree[(root << 1) | 1].val;
}
//求区间和
ll query(ll root, ll left, ll right){
	if (tree[root].l >= left&&tree[root].r <= right)
		return tree[root].val;
	if (tree[root].l > right || tree[root].r < left)
		return 0;
	if (tree[root].lazy) pushdown(root);
	return query(root << 1, left, right) + query((root << 1) | 1, left, right);
}
int main(){
	ll x, y, z, k;
	ll n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		arr[i] = i;
	buildtree(1, 1, n);
	for (int i = 1; i <= m; i++){
		cin >> z;
		if (z == 1){
			cin >> x >> y;
			update(1, x, y);
		}
		else{
			cin >> x >> y;
			cout << query(1, x, y) << endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值