分析
第一次写二维线段树,四叉树的写法。改了将近两个小时,结果最后发现把 long long 去掉时间复杂度就行了,哭掉。
这是图解。摘自一篇博客。
也就是每一个线段树的子节点,代表一个矩形,它的四个儿子,分别代表它的左上,左下,右上,右下。
其它方面就和普通的线段树无异了,唯一的变化就是你的处理由跑左儿子和右儿子,变成了跑四个儿子。
对于本题的话,题意即将一个矩形内的数全部修改为原来矩形中的最大值加上一个给出的值。
由于时间很卡,非常的卡(对我来说)。我们考虑标记永久化,以及标记单增性。
即你这个位置的标记,对你下面的东西永久生效,在询问最大值时,你可以相当于把所有标记一起带着问,这样就不需要反复地去 pushdown,以及标记单增性,即可以直接覆盖。
另外一个可以节省时间的就是:query 和 modify 的过程是一模一样的,只是最终执行的操作不同,因此询问时可以将要修改的节点提前存储,这样可以避免在修改时访问多个儿子。
代码
#include<bits/stdc++.h>
using namespace std;
inline void read(int &res){
char c=getchar();
res=0;
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
}
int n,d,s;
int tot,summ;
int mx[5000000],tag[5000005];
int x2,xx2,y2,yy2,k;
inline int max(int aa,int bb){return aa>bb?aa:bb;}
int q[10000000],head;
int qq[10000000],headd;
inline int query(int rt,int x1,int xx1,int y1,int yy1){
q[++head]=rt;//需更新mx的
if(x1>=x2&&xx1<=xx2&&y1>=y2&&yy1<=yy2){
qq[++headd]=rt;//需更新tag的
return mx[rt];
}
int mid=x1+xx1>>1,midd=y1+yy1>>1,ans=tag[rt];//标记永久化
if(mid>=x2&&midd>=y2)ans=max(ans,query(rt<<2|1,x1,mid,y1,midd));
if(mid<xx2&&midd>=y2)ans=max(ans,query(rt<<2|2,mid+1,xx1,y1,midd));
if(mid>=x2&&midd<yy2)ans=max(ans,query(rt<<2|3,x1,mid,midd+1,yy1));
if(mid<xx2&&midd<yy2)ans=max(ans,query((rt<<2)+4,mid+1,xx1,midd+1,yy1));
return ans;
}
inline void modify(){
int x;
while(head)mx[x]=max(mx[x=q[head--]],k);
while(headd)tag[qq[headd--]]=k;
}
signed main(){
read(d);read(s);read(n);
int xx,yy,x0,y0;
//因为没有初始所需赋的值,所以不需build
for(int i=1;i<=n;i++){
read(xx);read(yy);read(k);read(x0);read(y0);
x2=x0+1,xx2=x0+xx,y2=y0+1,yy2=y0+yy;
//注意题目描述中是左上角点的标号,因此应是x0+1个矩形起始
k+=query(0,1,d,1,s);
modify();
}
cout<<mx[0]<<endl;
return 0;
}