@luogu - P6109@ [Ynoi2009]rprmq
@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的更多相关文章
- Luogu 魔法学院杯-第二弹(萌新的第一法blog)
虽然有点久远 还是放一下吧. 传送门:https://www.luogu.org/contest/show?tid=754 第一题 沉迷游戏,伤感情 #include <queue> ...
- luogu p1268 树的重量——构造,真正考验编程能力
题目链接:http://www.luogu.org/problem/show?pid=1268#sub -------- 这道题费了我不少心思= =其实思路和标称毫无差别,但是由于不习惯ACM风格的题 ...
- [luogu P2170] 选学霸(并查集+dp)
题目传送门:https://www.luogu.org/problem/show?pid=2170 题目描述 老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一 ...
- [luogu P2647] 最大收益(贪心+dp)
题目传送门:https://www.luogu.org/problem/show?pid=2647 题目描述 现在你面前有n个物品,编号分别为1,2,3,--,n.你可以在这当中任意选择任意多个物品. ...
- Luogu 考前模拟Round. 1
A.情书 题目:http://www.luogu.org/problem/show?pid=2264 赛中:sb题,直接暴力匹配就行了,注意一下读入和最后一句话的分句 赛后:卧槽 怎么只有40 B.小 ...
- luogu P2580 于是他错误的点名开始了
luogu P2580 于是他错误的点名开始了 https://www.luogu.org/problem/show?pid=2580 题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 他会一边 ...
- CJOJ 1331 【HNOI2011】数学作业 / Luogu 3216 【HNOI2011】数学作业 / HYSBZ 2326 数学作业(递推,矩阵)
CJOJ 1331 [HNOI2011]数学作业 / Luogu 3216 [HNOI2011]数学作业 / HYSBZ 2326 数学作业(递推,矩阵) Description 小 C 数学成绩优异 ...
- Luogu 1349 广义斐波那契数列(递推,矩阵,快速幂)
Luogu 1349 广义斐波那契数列(递推,矩阵,快速幂) Description 广义的斐波那契数列是指形如\[A_n=p*a_{n-1}+q*a_{n-2}\]的数列.今给定数列的两系数p和q, ...
- Luogu 1962 斐波那契数列(矩阵,递推)
Luogu 1962 斐波那契数列(矩阵,递推) Description 大家都知道,斐波那契数列是满足如下性质的一个数列: f(1) = 1 f(2) = 1 f(n) = f(n-1) + f(n ...
随机推荐
- poj2778 AC自动机
以下内容均为转载,,只有代码是自己写的=-= http://blog.csdn.net/morgan_xww/article/details/7834801 转载地址 博主写的很好 ------- ...
- 3.Linux如何管理分区
上一次谈完了硬盘与分区的基础知识,下面谈一下Linux如何管理分区. Linux管理硬件和windows完全不同.任何东西(包括硬件)在Linux看来都是文件设备,有字符和二进制形式的设备.如打印机. ...
- 读Pyqt4教程,带你入门Pyqt4 _005
对话框窗体或对话框是现代GUI应用不可或缺的一部分.dialog定义为两个或多个人之间的交谈.在计算机程序中dialog是一个窗体,用来和程序“交谈”.对话框用来输入数据.修改数据.改变程序设置等等. ...
- [Wireshark]_003_电子邮件抓包分析
电子邮件是我们的生活工作中经常使用的一种服务,用来联系世界各地的朋友,客户.下面我们就用Wireshark对电子邮件进行抓包. 准备工作: 邮件客户端一款(Outlook,Foxmail,KooMai ...
- Docker 笔记一相关命令
Centos 7 : Service network restart 重启网络 Ip addr 查看ip地址 Uname -r 查看内核版本 Yum install docker 安装docker 命 ...
- MySQL select from join on where group by having order by limit 执行顺序
书写顺序:select [查询列表] from [表] [连接类型] join [表2] on [连接条件] where [筛选条件] group by [分组列表] having [分组后的筛选条件 ...
- SDL初识
1.SDL是什么? SDL(Security Development Lifecycle)安全开发生命周期.是微软提出的从安全角度指导软件开发的管理模式,在软件开发的生命周期中尽可能地发现安全隐患,降 ...
- vscode jshint 报'import' is only available in ES6 (use 'esversion: 6'). (W119)错误
vue项目用vscode打开代码前出现黄点,js报错 'import' is only available in ES6 (use 'esversion: 6'). (W119) 意思是import属 ...
- Rocket - config - Keys & Params
https://mp.weixin.qq.com/s/Y42EWrO7IoHRD_yHD4iRLA 介绍配置项.配置值以及他们的使用方式. 参考链接: https://docs.qq.com/ ...
- Chisel3 - bind - Binding
https://mp.weixin.qq.com/s/2318e6VJ4wFGpWwBOmTikA Chisel数据类型(Data)与Module的绑定关系,根据Data的使用方式不同,而有多种绑 ...