温暖的签到题
时间限制: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;
}