数组那么大,显然不能使用差分。
如果使用二维数据结构,时间复杂度为$((n+q) log_2^2 n)$,由于$q$是$500000$级别的,所以肯定过不去。
考虑在$x$轴方向建立一棵线段树(实际上是分治树)。每个询问把它挂在线段树上满足跨过当前区间中线且被包含于当前区间的节点。
实际上,$max$操作不满足可减性,所以只能把它按照$x$坐标拆成$[l,md]$,$[md+1,r]$的两个矩形求解。
先考虑求解$[md+1,r]$。按照$x$坐标从小到大做扫描线。每个加法操作拆成在左端点$+$值,右端点+1$-$值。
维护一个可以区间加,求区间历史最大值,划分$y$轴的线段树(cpu监控简化版),且支持把区间最大值变成现在最大值。
先把当前$[1,md+1]$操作全部加上。(实际上在程序内只要加$[l,md+1]$,原因等会说)
再把当前区间历史最大值变成最大值(因为前面的点未被覆盖,不能贡献答案)
然后从左到右扫的时候,当前询问其实就是要求在时间$[md+1,r]$加入的操作的历史最大值。使用这个$y$轴线段树统计答案。
然后把$[md+1,r]$操作全部撤回
然后递归计算$x$轴线段树右儿子(因为可以直接利用线段树的信息,现在前面$[1,l]~[l,md]$都被加进线段树了)
接下来统计区间$[l,md]$的答案时,应当按照$x$值逆序做扫描线。每次将当前值$i$的操作撤销再统计答案。
然后统计$[l,md]$(因为可以直接利用线段树信息,现在前面$[1,l]$都被加进线段树了)
注意,下传标记要先下传“把区间最大值变成现在最大值”标记,再下传区间加标记。因为区间加的标记会对下面的最大值产生影响。
注意,在统计答案时,要先修改,再查询。
时间复杂度:在当前点会扫描当前区间2遍并且修改会遍历当前区间2遍,查询是$O(qlog_2 n)$,扫描是$O(n log_2^2 n)$
总时间是$O(qlog_2 n+n log_2^2 n)$
总结:在做题的时候,有些时候要利用莫队思想,即利用以前的信息进行回答询问。
本人的cpu监控的代码速度十分慢,比最快速度要慢10倍,所以直接复制粘贴了另一个人的。
#include<bits/stdc++.h> using namespace std; #define int long long #define ll long long #define N 50010 int n,m,qu; struct no{ int l,r,x; }; struct nn{ int l,r,x,y,i; }a[N*10]; int ans[N*10]; vector<no>v[N]; vector<nn>q[N*15]; inline void ckmax(int &x, int y) {x=max(x,y);} struct Segtree { struct node { int max, add, Max, Add; bool res; }t[N << 3]; #define lc (o << 1) #define rc (o << 1 | 1) void pushup(int o) { t[o].max = max(t[lc].max, t[rc].max); t[o].Max = max(t[lc].Max, t[rc].Max); } void dadd(int o, ll x, ll y) { ckmax(t[o].Max, t[o].max + x); t[o].max += y; ckmax(t[o].Add, t[o].add + x), t[o].add += y; } void dres(int o) { if(t[o].add || t[o].Add) { dadd(lc, t[o].Add, t[o].add); dadd(rc, t[o].Add, t[o].add); t[o].Add = t[o].add = 0; } t[o].Max = t[o].max; t[o].Add = t[o].add; t[o].res = 1; } void pushdown(int o) { if(t[o].res) { dres(lc); dres(rc); t[o].res = 0; } if(t[o].add || t[o].Add) { dadd(lc, t[o].Add, t[o].add); dadd(rc, t[o].Add, t[o].add); t[o].Add = t[o].add = 0; } } void update(int o, int l, int r, int pl, int pr, int x) { if(l >= pl && r <= pr) { dadd(o, x, x); return; } pushdown(o); int mid = (l + r) >> 1; if(pl <= mid) update(lc, l, mid, pl, pr, x); if(pr > mid) update(rc, mid + 1, r, pl, pr, x); pushup(o); } ll query(int o, int l, int r, int pl, int pr) { if(l >= pl && r <= pr) return t[o].Max; pushdown(o); int mid = (l + r) >> 1; if(pr <= mid) return query(lc, l, mid, pl, pr); if(pl > mid) return query(rc, mid + 1, r, pl, pr); return max(query(lc, l, mid, pl, pr), query(rc, mid + 1, r, pl, pr)); } }T; void mod(int o,int l,int r,nn x){ int md=(l+r)/2; if(x.l<=md+1&&md<=x.r){ q[o].push_back(x); return; } if(x.r<=md)mod(o*2,l,md,x); else mod(o*2+1,md+1,r,x); } void up(int o,int t){ if(t==1){ for(auto x:v[o]) T.update(1,1,n,x.l,x.r,x.x); } else{ for(int i=(int)v[o].size()-1;~i;i--) T.update(1,1,n,v[o][i].l,v[o][i].r,-v[o][i].x); } } int cp(no x,no y){ return x.x<y.x; } int c1(nn x,nn y){ return x.r<y.r; } int c2(nn x,nn y){ return x.l>y.l; } void dfs(int o,int l,int r){ int md=(l+r)/2; for(int i=l;i<=md;i++) up(i,1); int ct=0,j=1; for(auto x:q[o])a[++ct]=x; sort(a+1,a+ct+1,c1); while(j<=ct&&a[j].r==md)j++; for(int i=md+1;i<=r;i++){ up(i,1); if(i==md+1)T.dres(1); for(;j<=ct&&a[j].r==i;j++) ans[a[j].i]=max(ans[a[j].i],T.query(1,1,n,a[j].x,a[j].y)); } for(int i=r;i>md;i--) up(i,-1); if(l!=r)dfs(o*2+1,md+1,r); ct=0,j=1; for(auto x:q[o])a[++ct]=x; sort(a+1,a+ct+1,c2); while(j<=ct&&a[j].l==md+1)j++; for(int i=md;i>=l;i--){ if(i==md)T.dres(1); for(;j<=ct&&a[j].l==i;j++) ans[a[j].i]=max(ans[a[j].i],T.query(1,1,n,a[j].x,a[j].y)); up(i,-1); } if(l!=r)dfs(o*2,l,md); } signed main(){ cin>>n>>m>>qu; for(int i=1;i<=m;i++){ int l,r,x,y,z; scanf("%lld%lld%lld%lld%lld",&l,&x,&r,&y,&z); v[l].push_back((no){x,y,z}); v[r+1].push_back((no){x,y,-z}); } for(int i=1;i<=qu;i++){ int l,x,r,y; scanf("%lld%lld%lld%lld",&l,&x,&r,&y); mod(1,1,n,(nn){l,r,x,y,i}); } for(int i=1;i<=n;i++) sort(v[i].begin(),v[i].end(),cp); dfs(1,1,n); for(int i=1;i<=qu;i++) printf("%lld ",ans[i]); }