@description@

有一个 n×n 的矩阵 a,初始全是 0,有 m 次修改操作和 q 次查询操作,先进行所有修改操作,然后进行所有查询操作。

一次修改操作会给出 l1,l2,r1,r2,x,代表把所有满足 l1≤i≤r1 且 l2≤j≤r2 的 a[i][j] 元素加上一个值 x。

一次查询操作会给出 l1,l2,r1,r2,代表查询所有满足 l1≤i≤r1 且 l2≤j≤r2 的 a[i][j] 元素的最大值。

原题链接。

@solution@

考虑扫描线,修改操作看成在 l1 处执行区间 (l2, r2) 加 x,r1 + 1 处执行区间 (l2, r2) 减 x。

这样可以做到 O((m + n)*log + q*n)。

尝试以提高修改复杂度为代价降低查询复杂度。

枚举询问左端点 l1',从最左边(注意这里是最左边)左往右作扫描线,并从 l1' 开始维护历史最大值。

这样只需在 r1' 处查询区间 [l2', r2'] 历史最大值即可。可以做到 O((m + n)*n*log + q*log)。

因为 n, m 和 q 的范围并不对等,所以 O(q*log) 对于 q 而言是能接受的最大复杂度。

考虑一种分治:每次分治到 [L, R] 时仅处理经过中点 mid 的询问,将询问拆成 [11', mid] + [mid, r1']。

一次从 mid 出发,从左往右扫描;一次从 mid 出发,从右往左扫描,并维护历史最大值。

不过我们扫描线必须要从最左边/最右边开始(否则就会出现奇奇怪怪的东西),但这样复杂度没法保证。

可以开两棵可持久化线段树(使用标记永久化),用 mid 处的版本去初始化我们要维护历史最大值的线段树。

这里的初始化使用 tag 的方法,不然复杂度也会爆炸。。。

然后复杂度应该是 O(m*log^2 + (n + q)*log),就可以通过此题了。

有一个小细节。某一时刻我们必须先加入该时刻的区间减操作再加入该时刻的区间加操作。

否则维护历史最大值可能就是出现只取这一时刻的一部分操作,然而这是不合法的。

@accepted code@

#include <cstdio>
#include <vector>
using namespace std; typedef long long ll; const int MAXN = 50000;
const int MAXQ = 500000; struct segtree1{
struct node{
ll tag, mx;
node *ch[2];
}pl[100*MAXN + 5], *ncnt, *NIL;
segtree1() {
ncnt = NIL = pl;
NIL->tag = NIL->mx = 0, NIL->ch[0] = NIL->ch[1] = NIL;
}
node *newnode() {
node *p = (++ncnt);
p->tag = p->mx = 0, p->ch[0] = p->ch[1] = NIL;
return p;
}
node *modify(node *pre, int l, int r, int ql, int qr, ll d) {
if( qr < l || ql > r ) return pre;
node *nw = newnode(); (*nw) = (*pre);
if( ql <= l && r <= qr )
nw->mx += d, nw->tag += d;
else {
int m = (l + r) >> 1;
nw->ch[0] = modify(pre->ch[0], l, m, ql, qr, d);
nw->ch[1] = modify(pre->ch[1], m + 1, r, ql, qr, d);
nw->mx = max(nw->ch[0]->mx, nw->ch[1]->mx) + nw->tag;
}
return nw;
}
}T1, T2;
segtree1::node *rt1[MAXN + 5], *rt2[MAXN + 5]; struct segtree2{
#define lch (x << 1)
#define rch (x << 1 | 1) int le[4*MAXN + 5], ri[4*MAXN + 5];
bool dt[4*MAXN + 5]; segtree1::node *nd[4*MAXN + 5];
ll tg[4*MAXN + 5], htg[4*MAXN + 5], mx[4*MAXN + 5], hmx[4*MAXN + 5]; void build(int x, int l, int r) {
le[x] = l, ri[x] = r;
if( l == r ) return ;
int m = (l + r) >> 1;
build(lch, l, m), build(rch, m + 1, r);
}
void maintain1(int x, segtree1::node *t) {
dt[x] = true;
hmx[x] = mx[x] = t->mx;
htg[x] = tg[x] = 0;
nd[x] = t;
}
void maintain2(int x, ll hd, ll d) {
htg[x] = max(htg[x], tg[x] + hd), tg[x] += d;
hmx[x] = max(hmx[x], mx[x] + hd), mx[x] += d;
}
void pushup(int x) {
hmx[x] = max(hmx[lch], hmx[rch]) + nd[x]->tag;
mx[x] = max(mx[lch], mx[rch]) + nd[x]->tag;
}
void pushdown(int x) {
if( dt[x] ) {
maintain1(lch, nd[x]->ch[0]);
maintain1(rch, nd[x]->ch[1]);
dt[x] = false;
}
if( tg[x] || htg[x] ) {
maintain2(lch, htg[x], tg[x]);
maintain2(rch, htg[x], tg[x]);
tg[x] = htg[x] = 0;
}
}
void modify(int x, int l, int r, ll d) {
if( l > ri[x] || r < le[x] )
return ;
if( l <= le[x] && ri[x] <= r ) {
maintain2(x, d, d);
return ;
}
pushdown(x);
modify(lch, l, r, d);
modify(rch, l, r, d);
pushup(x);
}
ll query(int x, int l, int r) {
if( l > ri[x] || r < le[x] ) return 0;
if( l <= le[x] && ri[x] <= r ) return hmx[x];
pushdown(x);
return max(query(lch, l, r), query(rch, l, r)) + nd[x]->tag;
}
}t1, t2; struct query{
int l1, r1, l2, r2, id;
}qry[MAXQ + 5];
ll ans[MAXQ + 5]; struct node{
int l, r, x; node() : l(0), r(0), x(0) {}
node(int _l, int _r, int _x) : l(_l), r(_r), x(_x) {}
};
vector<node>v1[MAXN + 5], v2[MAXN + 5], vl[MAXN + 5], vr[MAXN + 5];
void solve(int l, int r, int k) {
if( !k ) return ;
int m = (l + r) >> 1;
for(int i=l;i<=r;i++) vl[i].clear(), vr[i].clear();
bool flag = false;
for(int i=1;i<=k;i++)
if( qry[i].l1 <= m && m <= qry[i].r1 ) {
vl[qry[i].l1].push_back(node(qry[i].l2, qry[i].r2, qry[i].id));
vr[qry[i].r1].push_back(node(qry[i].l2, qry[i].r2, qry[i].id));
flag = true;
}
t1.maintain1(1, rt1[m]);
for(int j=0;j<(int)vr[m].size();j++) {
node t = vr[m][j];
ans[t.x] = max(ans[t.x], t1.query(1, t.l, t.r));
}
for(int i=m+1;i<=r;i++) {
for(int j=0;j<(int)v1[i].size();j++) {
node t = v1[i][j];
t1.modify(1, t.l, t.r, t.x);
}
for(int j=0;j<(int)vr[i].size();j++) {
node t = vr[i][j];
ans[t.x] = max(ans[t.x], t1.query(1, t.l, t.r));
}
}
t2.maintain1(1, rt2[m]);
for(int j=0;j<(int)vl[m].size();j++) {
node t = vl[m][j];
ans[t.x] = max(ans[t.x], t2.query(1, t.l, t.r));
}
for(int i=m-1;i>=l;i--) {
for(int j=0;j<(int)v2[i].size();j++) {
node t = v2[i][j];
t2.modify(1, t.l, t.r, t.x);
}
for(int j=0;j<(int)vl[i].size();j++) {
node t = vl[i][j];
ans[t.x] = max(ans[t.x], t2.query(1, t.l, t.r));
}
} int p = 0;
for(int i=1;i<=k;i++)
if( qry[i].r1 < m )
swap(qry[++p], qry[i]);
solve(l, m - 1, p);
p = 0;
for(int i=1;i<=k;i++)
if( qry[i].l1 > m )
swap(qry[++p], qry[i]);
solve(m + 1, r, p);
} int n, m, q; int read() {
int x = 0; char ch = getchar();
while( ch > '9' || ch < '0' ) ch = getchar();
while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
return x;
}
void write(ll x) {
if( !x ) return ;
write(x / 10), putchar(x % 10 + '0');
}
query arr[MAXN + 5];
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
n = read(), m = read(), q = read();
for(int i=1;i<=m;i++) {
arr[i].l1 = read(), arr[i].l2 = read();
arr[i].r1 = read(), arr[i].r2 = read(), arr[i].id = read();
v1[arr[i].r1+1].push_back(node(arr[i].l2, arr[i].r2, -arr[i].id));
v2[arr[i].l1-1].push_back(node(arr[i].l2, arr[i].r2, -arr[i].id));
}
for(int i=1;i<=m;i++) {
v1[arr[i].l1].push_back(node(arr[i].l2, arr[i].r2, arr[i].id));
v2[arr[i].r1].push_back(node(arr[i].l2, arr[i].r2, arr[i].id));
}
rt1[0] = T1.NIL, rt2[n + 1] = T2.NIL;
for(int i=1;i<=n;i++) {
rt1[i] = rt1[i-1];
for(int j=0;j<(int)v1[i].size();j++) {
node t = v1[i][j];
rt1[i] = T1.modify(rt1[i], 1, n, t.l, t.r, t.x);
}
}
for(int i=n;i>=1;i--) {
rt2[i] = rt2[i+1];
for(int j=0;j<(int)v2[i].size();j++) {
node t = v2[i][j];
rt2[i] = T2.modify(rt2[i], 1, n, t.l, t.r, t.x);
}
}
for(int i=1;i<=q;i++)
qry[i].l1 = read(), qry[i].l2 = read(), qry[i].r1 = read(), qry[i].r2 = read(), qry[i].id = i;
t1.build(1, 1, n), t2.build(1, 1, n), solve(1, n, q);
for(int i=1;i<=q;i++) {
if( ans[i] == 0 ) puts("0");
else write(ans[i]), puts("");
}
}

@details@

被喊去打洛谷月赛,因为是第一次打有一点小紧张。

紧张到把 div1D 看成了 div1C 然后在那里瞎搞半天结果赛后一小时才 AC = =。

@luogu - P6109@ [Ynoi2009]rprmq的更多相关文章

  1. Luogu 魔法学院杯-第二弹(萌新的第一法blog)

    虽然有点久远  还是放一下吧. 传送门:https://www.luogu.org/contest/show?tid=754 第一题  沉迷游戏,伤感情 #include <queue> ...

  2. luogu p1268 树的重量——构造,真正考验编程能力

    题目链接:http://www.luogu.org/problem/show?pid=1268#sub -------- 这道题费了我不少心思= =其实思路和标称毫无差别,但是由于不习惯ACM风格的题 ...

  3. [luogu P2170] 选学霸(并查集+dp)

    题目传送门:https://www.luogu.org/problem/show?pid=2170 题目描述 老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一 ...

  4. [luogu P2647] 最大收益(贪心+dp)

    题目传送门:https://www.luogu.org/problem/show?pid=2647 题目描述 现在你面前有n个物品,编号分别为1,2,3,--,n.你可以在这当中任意选择任意多个物品. ...

  5. Luogu 考前模拟Round. 1

    A.情书 题目:http://www.luogu.org/problem/show?pid=2264 赛中:sb题,直接暴力匹配就行了,注意一下读入和最后一句话的分句 赛后:卧槽 怎么只有40 B.小 ...

  6. luogu P2580 于是他错误的点名开始了

    luogu  P2580 于是他错误的点名开始了 https://www.luogu.org/problem/show?pid=2580 题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 他会一边 ...

  7. CJOJ 1331 【HNOI2011】数学作业 / Luogu 3216 【HNOI2011】数学作业 / HYSBZ 2326 数学作业(递推,矩阵)

    CJOJ 1331 [HNOI2011]数学作业 / Luogu 3216 [HNOI2011]数学作业 / HYSBZ 2326 数学作业(递推,矩阵) Description 小 C 数学成绩优异 ...

  8. Luogu 1349 广义斐波那契数列(递推,矩阵,快速幂)

    Luogu 1349 广义斐波那契数列(递推,矩阵,快速幂) Description 广义的斐波那契数列是指形如\[A_n=p*a_{n-1}+q*a_{n-2}\]的数列.今给定数列的两系数p和q, ...

  9. Luogu 1962 斐波那契数列(矩阵,递推)

    Luogu 1962 斐波那契数列(矩阵,递推) Description 大家都知道,斐波那契数列是满足如下性质的一个数列: f(1) = 1 f(2) = 1 f(n) = f(n-1) + f(n ...

随机推荐

  1. storm-redis 详解

    多的不说,先来代码分析,再贴我自己写的代码.如果代码有错误,求更正.. 导入两个关键包,其他项目需要的包,大家自己导入了,我pom下的包太多,不好一下扔上来. <dependency> & ...

  2. Spring 基于注解的配置 简介

    基于注解的配置 从 Spring 2.5 开始就可以使用注解来配置依赖注入.而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身. ...

  3. Java内存虚拟机理解

        对于Java程序员,在虚拟机自动内存管理机制的帮助下,不需要再为每一个操作写配对的释放资源操作,不容易出现内存泄露和内存溢出问题.加深对Java虚拟机的理解,有助于在发现问题时精准定位问题,排 ...

  4. 自定义reaml创建使用实现认证

    注意清空shiro.ini 创建User对象 package cn.zys.Bean; public class User { private Integer id; private String u ...

  5. 【深度学习】PyTorch之Squeeze()和Unsqueeze()

    1. unsqueeze() 该函数用来增加某个维度.在PyTorch中维度是从0开始的. import torch a = torch.arange(0, 9) print(a) 结果: tenso ...

  6. sqlmap基本使用

    sqlmap 使用 需要安装python2的环境 使用方法: 用最基础的get型注入开始 1.当我们发现注入点的时候, python sqlmap.py -u "http://192.168 ...

  7. 《腾讯网UED体验设计之旅》读后感

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  8. 我的第一个jQuery插件开发(日期选择器,datePicker),功能还不完善,但用于学习参考已经足够了。

    一.学习jQuery插件开发网上的帖子很多,插件开发的方式也有好几种,现在推荐一个帖子讲述的特别好,我也是这篇文张的基础上学习的. 参考:http://www.cnblogs.com/ajianbey ...

  9. Linux suid 提权

    SUID (Set owner User ID up on execution) 是给予文件的一个特殊类型的文件权限.在 Linux/Unix中,当一个程序运行的时候, 程序将从登录用户处继承权限.S ...

  10. 02 . Python之数据类型

    Python入门之数据类型 变量存储在内存中的值.这就意味着在创建变量时会在内存中开辟一个空间.基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中. 因此,变量可以指定不同的数 ...