洛谷 P6109 [Ynoi2009] rprmq
有一个 \(n \times n\) 的矩阵 \(a\),初始全是 \(0\),有 \(m\) 次修改操作和 \(q\) 次查询操作,先进行所有修改操作,然后进行所有查询操作。
一次修改操作会给出 \(l_1,l_2,r_1,r_2,x\),代表把所有满足 \(l_1 \le i \le r_1\) 且 \(l_2 \le j \le r_2\) 的 \(a_{i,j}\) 元素加上一个值 \(x\)。
一次查询操作会给出 \(l_1,l_2,r_1,r_2\),代表查询所有满足 \(l_1 \le i \le r_1\) 且 \(l_2 \le j \le r_2\) 的 \(a_{i,j}\) 元素的最大值。
\(1\leq n,m\leq 5\times 10^4\),\(1\leq q \leq 5\times 10^5\),\(1\leq x\leq 2147483647\),\(1\leq l_1\leq r_1\leq n\),\(1\leq l_2\leq r_2\leq n\)
考虑把修改操作拆成 \(l_1,r_1,1,r_2,x\) 和 \(l_1,r_1,1,l_2-1,-x\) ,于是我们关于 \(y\) 轴分治,对于一个位置 \(i\) ,会对 \(i\) 有影响的修改是 \([i,n]\) 的修改。
设当前区间 \([l,r]\) 的中点是 \(m\) ,我们考虑用二区间合并来求答案,那么每个位置的修改就可以看作扫描线的区间修改,用线段树维护,那么要从 \(m\) 分别向左右扩展,前后缀最大值可以看作线段树历史最大值。
对于 \([l,m]\) ,把 \([m+1,n]\) 的修改保留,从 \(m\) 倒序加入修改更新询问就行了;对于 \([m+1,r]\) ,考虑从 \(m+1\) 往后查询,然后删掉当前位置的贡献。
于是线段树我们要维护最大值和历史最大值,这个在维护加法标记的基础上再维护一个历史最大标记就可以了,然后在保留贡献的时候需要一个将历史最大值设成当前最大值的标记,分治的时候保留右区间的贡献分治左区间,删掉再分治右区间就可以了。
因为每个修改我们最多出现在 \(\log n\) 个区间,而查询是 \(\log n\) 的,那么复杂度就是 \(O(m\log^2n+q\log n)\) 。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
const int N = 5e4;
const int M = 5e5;
using namespace std;
struct modi
{
int x,y,h,v;
}p[N * 2 + 5];
struct qry
{
int l1,l2,l,r,id;
}q[M + 5],q1[M + 5],q2[M + 5];
int n,m,Q,pc;
long long ans[M + 5];
vector <int> d[N + 5],qu[M + 5];
struct node
{
long long x,tag,mx,htag;
bool rt;
};
struct Seg
{
node s[N * 4 + 5];
#define zrt k << 1
#define yrt k << 1 | 1
void pushup(int k)
{
s[k].mx = max(s[zrt].mx,s[yrt].mx);
s[k].x = max(s[zrt].x,s[yrt].x);
}
void add(int k,long long v,long long hv)
{
s[k].mx = max(s[k].mx,s[k].x + hv);
s[k].htag = max(s[k].htag,s[k].tag + hv);
s[k].tag += v;
s[k].x += v;
}
void reset(int k)
{
if (zrt <= n * 4)
{
add(zrt,s[k].tag,s[k].htag);
add(yrt,s[k].tag,s[k].htag);
}
s[k].tag = s[k].htag = 0;
s[k].mx = s[k].x;
s[k].rt = 1;
}
void pushdown(int k)
{
if (s[k].rt)
{
reset(zrt);
reset(yrt);
s[k].rt = 0;
}
add(zrt,s[k].tag,s[k].htag);
add(yrt,s[k].tag,s[k].htag);
s[k].tag = s[k].htag = 0;
}
void modify(int k,int l,int r,int x,int y,int z)
{
if (l >= x && r <= y)
{
add(k,z,z);
return;
}
int mid = l + r >> 1;
pushdown(k);
if (x <= mid)
modify(zrt,l,mid,x,y,z);
if (y > mid)
modify(yrt,mid + 1,r,x,y,z);
pushup(k);
}
long long query(int k,int l,int r,int x,int y)
{
if (l >= x && r <= y)
return s[k].mx;
int mid = l + r >> 1;
pushdown(k);
if (y <= mid)
return query(zrt,l,mid,x,y);
if (x > mid)
return query(yrt,mid + 1,r,x,y);
return max(query(zrt,l,mid,x,y),query(yrt,mid + 1,r,x,y));
}
}tree;
void solve(int l,int r,int ql,int qr)
{
if (ql > qr)
return;
if (l == r)
{
vector <int>::iterator it;
for (it = d[l].begin();it != d[l].end();it++)
{
int i = *it;
tree.modify(1,1,n,p[i].x,p[i].y,p[i].v);
}
tree.reset(1);
for (int i = ql;i <= qr;i++)
{
ans[q[i].id] = tree.query(1,1,n,q[i].l1,q[i].l2);
}
for (it = d[l].begin();it != d[l].end();it++)
{
int id = *it;
tree.modify(1,1,n,p[id].x,p[id].y,-p[id].v);
}
tree.reset(1);
return;
}
int mid = l + r >> 1,cnt1 = 0,cnt2 = 0;
vector <int>::iterator it;
for (int i = ql;i <= qr;i++)
if (q[i].l <= mid && q[i].r > mid)
{
qu[q[i].l].push_back(i);
qu[q[i].r].push_back(i);
}
else
{
if (q[i].r <= mid)
q1[++cnt1] = q[i];
if (q[i].l > mid)
q2[++cnt2] = q[i];
}
for (int i = mid + 1;i <= r;i++)
{
for (it = d[i].begin();it != d[i].end();it++)
{
int id = *it;
tree.modify(1,1,n,p[id].x,p[id].y,p[id].v);
}
}
for (int i = mid;i >= l;i--)
{
for (it = d[i].begin();it != d[i].end();it++)
{
int id = *it;
tree.modify(1,1,n,p[id].x,p[id].y,p[id].v);
}
if (i == mid)
tree.reset(1);
for (it = qu[i].begin();it != qu[i].end();it++)
{
int id = *it;
ans[q[id].id] = max(ans[q[id].id],tree.query(1,1,n,q[id].l1,q[id].l2));
}
qu[i].clear();
}
for (int i = l;i <= mid;i++)
for (it = d[i].begin();it != d[i].end();it++)
{
int id = *it;
tree.modify(1,1,n,p[id].x,p[id].y,-p[id].v);
}
tree.reset(1);
for (int i = mid + 1;i <= r;i++)
{
for (it = qu[i].begin();it != qu[i].end();it++)
{
int id = *it;
ans[q[id].id] = max(ans[q[id].id],tree.query(1,1,n,q[id].l1,q[id].l2));
}
qu[i].clear();
for (int j = d[i].size() - 1;j >= 0;j--)
{
int id = d[i][j];
tree.modify(1,1,n,p[id].x,p[id].y,-p[id].v);
}
}
for (int i = mid + 1;i <= r;i++)
for (it = d[i].begin();it != d[i].end();it++)
{
int id = *it;
tree.modify(1,1,n,p[id].x,p[id].y,p[id].v);
}
tree.reset(1);
for (int i = 1;i <= cnt1;i++)
q[ql + i - 1] = q1[i];
for (int i = 1;i <= cnt2;i++)
q[ql + cnt1 + i - 1] = q2[i];
solve(l,mid,ql,ql + cnt1 - 1);
for (int i = mid + 1;i <= r;i++)
for (it = d[i].begin();it != d[i].end();it++)
{
int id = *it;
tree.modify(1,1,n,p[id].x,p[id].y,-p[id].v);
}
tree.reset(1);
solve(mid + 1,r,ql + cnt1,ql + cnt1 + cnt2 - 1);
}
bool cmp(modi a,modi b)
{
return a.v < b.v;
}
inline int read()
{
int X(0),w(0);char ch(0);
while (!isdigit(ch)) w |= ch == '-',ch = getchar();
while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48),ch = getchar();
return w ? -X : X;
}
int main()
{
n = read();m = read();Q = read();
int l1,l2,r1,r2,x;
for (int i = 1;i <= m;i++)
{
l1 = read();r1 = read();l2 = read();r2 = read();x = read();
p[++pc] = (modi){l1,l2,r2,x};
if (r1 > 1)
p[++pc] = (modi){l1,l2,r1 - 1,-x};
}
sort(p + 1,p + pc + 1,cmp);
for (int i = 1;i <= pc;i++)
d[p[i].h].push_back(i);
for (int i = 1;i <= Q;i++)
{
q[i].l1 = read();q[i].l = read();q[i].l2 = read();q[i].r = read();
q[i].id = i;
}
solve(1,n,1,Q);
for (int i = 1;i <= Q;i++)
printf("%lld\n",ans[i]);
return 0;
}