@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. 索引 'GXHRCS.PK_A253' 或这类索引的分区处于不可用状态

    ORA-01502: 索引 'GXHRCS.PK_A253' 或这类索引的分区处于不可用状态 http://blog.sina.com.cn/s/blog_7ab8d2720101ozw6.html ...

  2. linux -- 一般使用经验(四)

    一.使用grep进行条件筛选(主要为日志) 1.grep  -E '条件1|条件2|条件3'   文件名 (cat -n 文件.log |grep -E '2020-01-16 15:24:48|条件 ...

  3. InnoDB存储引擎的高级特性大盘点

    InnoDB作为mysql数据库最常用的存储引擎,自然包含了其独有的很多特性.如相比于memory.MyISAM引擎,InnoDB支持行级锁.事务等都是比较重要的特性. 本文将盘点下InnoDB处理事 ...

  4. 分布式应用程序协调服务 ZooKeeper

    1.简介: ZooKeeper 是一个分布的.开源的协调服务,它主要是用来解决分布式应用中经常遇到的一些数据管理问题.统一命名服务.状态同步服务.集群管理.分布式应用配置项的管理等,简化分布式应用协调 ...

  5. uniapp打包发版到linux服务器步骤----H5端

    最近在写uni-app项目,项目打包部署到服务器后,搞了好一会一直打开是空白页,原来自己有几个地方疏忽了,现把步骤整理一下: 第1步:编辑配置 mainifest.json 文件 tip:运行的基础路 ...

  6. SPL常用迭代器

    ArrayIterator 熟悉使用seek()跳过元素 熟悉使用asort,ksort排序 <?php $fruits = array( 'apple'=>'apple value', ...

  7. Mysql数值类型,小数点后保留两个零

    如有不足请帮忙留言区补充谢谢~ 一,数值类型保留小数点后两个0 在存入数据时,应客户需求数值类型,比如钱数,分数等等需要精确到小数点后几位. 800存入时显示为800.00 方法:在建表时直接定义此数 ...

  8. MySQL所有的安装部署方式

    目录 一.前言 二.关于MySQL的安装 三.部署规划 3.1 服务器规划 3.2 数据库目录规划 四.准备工具 五.通用二进制包安装MySQL 5.1 上传MySQL通用二进制安装包到node7的/ ...

  9. Java IO(三)FileDescriptor

    Java IO(三)FileDescriptor 一.介绍 FileDescriptor 是文件描述符,用来表示开放文件.开放套接字等.当 FileDescriptor 表示文件时,我们可以通俗的将 ...

  10. 一文彻底搞懂BERT

    一.什么是BERT? 没错下图中的小黄人就是文本的主角Bert ,而红色的小红人你应该也听过,他就是ELMo.2018年发布的BERT 是一个 NLP 任务的里程碑式模型,它的发布势必会带来一个 NL ...