正睿noip10连DAY9 T2题解(线段树)

题目描述

        有一个长度为 1 0 9 10^9 109 的序列 ,初始均为 0 0 0。给定 n n n 个序列上的区间 [ l i , r i ] [l_i,r_i] [li,ri]。进行 m m m 次操作,每次给定 p p p , x x x ,表示让 a p a_p ap 加上 x x x。每次操作之后,令区间 [ l i , r i ] [l_i,r_i] [li,ri] 的权值为 ∑ j = l i r i = a j ∑_{j = l_i}^{r_i}= a_j j=liri=aj,你需要找到这 n n n 区间中的最大权值。

        1 ≤ n , m ≤ 4 × 1 0 5 , 1 ≤ l i ≤ r i ≤ 1 0 9 , 1 ≤ p ≤ 1 0 9 , 0 ≤ x ≤ 1 0 9 1 \leq n,m \leq 4 \times 10^5,1 \leq l_i \leq r_i \leq 10^9,1 \leq p \leq 10^9,0 \leq x \leq 10^9 1nm4×1051liri1091p1090x109

分析:

        这需要分析一些性质:如果区间都 不相交,那么很显然每次操作最多只需要修改一个区间的值就行了。然后更新完后维护最大值就好了。

        如果不保证区间不想交怎么办?有一条性质是 如果一个区间被另一个区间包含,那么它一定对答案没有贡献。因为每次修改都只会增大区间的权值,而所有落在它里面的修改也都会落在包含它的区间里面。所以我们可以直接把这样的线段删除。

        删除这样的线段后,我们把其它线段按照左端点排序,那么所有的线段都满足 左端点递增,右端点递增。我们考虑一次修改会把覆盖这个点的线段权值加上 x x x,而这样的线段在排完序后显然 编号是连续的。我们很容易能够通过 二分 把编号的左右端点求出来,然后维护一棵 支持区间加,查询区间最大值 的线段树就好了。时间复杂度 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

CODE:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 4e5 + 10;
inline int read(){
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){if(c == '-') f = -1; c = getchar();}
	while(isdigit(c)){x = (x << 1) + (x << 3) + (c ^ 48); c = getchar();}
	return x * f;
}
LL x;
int n, m, tot, p;
struct seg{
	int l, r;
}a[N], b[N];
struct SegmentTree{
	int l, r; LL Maxn, tag;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define Maxn(x) t[x].Maxn
    #define tag(x) t[x].tag
}t[N * 4];
bool cmp(seg x, seg y){
	return ((x.l < y.l) || (x.l == y.l && x.r > y.r));
}
void build(int p, int l, int r){
	l(p) = l, r(p) = r;
	if(l == r) return ;
	int mid = (l + r >> 1);
	build(p << 1, l, mid);
	build(p << 1 | 1, mid + 1, r);
}
int Find(int p, int t){
	if(t == 0){
		int lt = 1, rt = tot, mid, res = -1;
		while(lt <= rt){
			mid = (lt + rt >> 1);
			if(b[mid].r >= p){
				if(b[mid].l <= p) res = mid;
				rt = mid - 1;
			}
			else lt = mid + 1;
		}
		return res;
	}
	else{
		int lt = 1, rt = tot, mid, res = -1;
		while(lt <= rt){
			mid = (lt + rt >> 1);
			if(b[mid].l <= p){
				if(b[mid].r >= p) res = mid;
				lt = mid + 1;
			}
			else rt = mid - 1;
		}
		return res;
	}
}
void update(int p){Maxn(p) = max(Maxn(p << 1), Maxn(p << 1 | 1));}
void spread(int p){
	if(tag(p)){
		tag(p << 1) += tag(p); Maxn(p << 1) += tag(p);
		tag(p << 1 | 1) += tag(p); Maxn(p << 1 | 1) += tag(p);
		tag(p) = 0; 
	}
}
void change(int p, int l, int r, LL c){
	if(l <= l(p) && r >= r(p)){
		Maxn(p) += c;
		tag(p) += c;
		return ;
	}
	spread(p);
	int mid = (l(p) + r(p) >> 1);
	if(l <= mid) change(p << 1, l, r, c);
	if(r > mid) change(p << 1 | 1, l, r, c);
	update(p);
}
int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i++){
		a[i].l = read(), a[i].r = read();
	}
	sort(a + 1, a + n + 1, cmp);
	for(int i = 1; i <= n; i++){
		if(a[i].r > b[tot].r) b[++tot] = a[i];
	}
	build(1, 1, tot);
	for(int i = 1; i <= m; i++){
		p = read(), x = 1LL * read();
		int l = Find(p, 0), r = Find(p, 1);
		if(l == -1 || r == -1){
			printf("%lld\n", Maxn(1));
			continue;
		}
		change(1, l, r, x);
		printf("%lld\n", Maxn(1));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值