题目描述
有一个长度为 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 1≤n,m≤4×105,1≤li≤ri≤109,1≤p≤109,0≤x≤109。
分析:
这需要分析一些性质:如果区间都 不相交,那么很显然每次操作最多只需要修改一个区间的值就行了。然后更新完后维护最大值就好了。
如果不保证区间不想交怎么办?有一条性质是 如果一个区间被另一个区间包含,那么它一定对答案没有贡献。因为每次修改都只会增大区间的权值,而所有落在它里面的修改也都会落在包含它的区间里面。所以我们可以直接把这样的线段删除。
删除这样的线段后,我们把其它线段按照左端点排序,那么所有的线段都满足 左端点递增,右端点递增。我们考虑一次修改会把覆盖这个点的线段权值加上 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;
}