[BZOJ2874]训练士兵
题目大意:
给你一个n*m的矩阵,区间修改d次,区间询问q次。
所有修改修完以后询问。
思路:
不难想到一个二维线段树的做法。
每次把当前平面分成四块,时间复杂度O(dp log_4(nm)),好像没什么问题。
于是以为自己要A了。
考完以后发现自己只有10分,90分MLE。连暴力都不如。
算了一下空间复杂度,O(nm log_4(nm)),不爆才怪。
这题正解是用主席树维护类似二维前缀和的东西。
每次区间修改相当于对4个顶点修改。
询问也是相当于对4个顶点询问。
首先对所有修改的顶点排序按横坐标排序。
考虑不同位置的修改对一个询问顶点作出的贡献。
对于一个顶点(X,Y),左上、左下、右上、右下四个方向的不同顶点有不同的贡献。
假设原点在左上角。
那么在(X,Y)左上方的点,贡献为x*y*s。
在(X,Y)左下方的点,贡献为x*Y*s。
在(X,Y)右上方的点,贡献为X*y*s。
在(X,Y)右下方的点,贡献为X*Y*s。
对于每一个修改顶点,我们记录s、x*s、y*s、x*y*s的值,并用主席树维护。
对于每个询问顶点,我们可以分左右两边讨论统计。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 #include<algorithm> 5 typedef unsigned long long qword; 6 inline int getint() { 7 register char ch; 8 while(!isdigit(ch=getchar())); 9 register int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 const int D=40000,SIZE=4500000; 14 struct Addition { 15 int x,y,s; 16 bool operator < (const Addition &another) const { 17 return x<another.x||(x==another.x&&y<another.y); 18 } 19 }; 20 Addition a[D<<2|1]; 21 int cnt; 22 inline void add_addition(const int &x,const int &y,const int &s) { 23 if(!x||!y) return; 24 a[++cnt]=(Addition){x,y,s}; 25 } 26 int n,m,d,q; 27 class FotileTree { 28 private: 29 struct Node { 30 int left,right; 31 qword s,xs,ys,xys; 32 }; 33 Node node[SIZE]; 34 int sz,new_node() { 35 return ++sz; 36 } 37 void push_up(const int &p) { 38 node[p].s=node[node[p].left].s+node[node[p].right].s; 39 node[p].xs=node[node[p].left].xs+node[node[p].right].xs; 40 node[p].ys=node[node[p].left].ys+node[node[p].right].ys; 41 node[p].xys=node[node[p].left].xys+node[node[p].right].xys; 42 } 43 public: 44 int root[D<<2|1]; 45 int modify(const int &p,const int &b,const int &e,const int &x,const int &y,const int &s) { 46 int new_p=new_node(); 47 node[new_p]=node[p]; 48 if(b==e) { 49 node[new_p].s+=s; 50 node[new_p].xs+=(qword)x*s; 51 node[new_p].ys+=(qword)y*s; 52 node[new_p].xys+=(qword)x*y*s; 53 return new_p; 54 } 55 const int mid=(b+e)>>1; 56 if(y<=mid) node[new_p].left=modify(node[p].left,b,mid,x,y,s); 57 if(y>mid) node[new_p].right=modify(node[p].right,mid+1,e,x,y,s); 58 push_up(new_p); 59 return new_p; 60 } 61 void query(const int &p1,const int &p2,const int &b,const int &e,const int &l,const int &r,qword &s,qword &xs,qword &ys,qword &xys) const { 62 if(!p2) return; 63 if(b==l&&e==r) { 64 s+=node[p2].s-node[p1].s; 65 xs+=node[p1].xs; 66 ys+=node[p2].ys-node[p1].ys; 67 xys+=node[p1].xys; 68 return; 69 } 70 const int mid=(b+e)>>1; 71 if(l<=mid) query(node[p1].left,node[p2].left,b,mid,l,std::min(r,mid),s,xs,ys,xys); 72 if(r>mid) query(node[p1].right,node[p2].right,mid+1,e,std::max(l,mid+1),r,s,xs,ys,xys); 73 } 74 }; 75 FotileTree t; 76 inline qword query(const int &x,const int &y) { 77 if(!x||!y) return 0; 78 qword ans=0,s=0,xs=0,ys=0,xys=0; 79 const int pos=std::upper_bound(&a[1],&a[cnt+1],(Addition){x,y,0})-&a[1]; 80 t.query(t.root[pos],t.root[cnt],1,m,1,y,s,xs,ys,xys); 81 ans+=xys+ys*x; 82 s=xs=ys=xys=0; 83 if(y<m) t.query(t.root[pos],t.root[cnt],1,m,y+1,m,s,xs,ys,xys); 84 ans+=xs*y+s*x*y; 85 return ans; 86 } 87 int main() { 88 n=getint(),m=getint(),d=getint(),q=getint(); 89 for(register int i=0;i<d;i++) { 90 const int x1=getint(),x2=getint(),y1=getint(),y2=getint(),s=getint(); 91 add_addition(x1-1,y1-1,s); 92 add_addition(x1-1,y2,-s); 93 add_addition(x2,y1-1,-s); 94 add_addition(x2,y2,s); 95 } 96 std::sort(&a[1],&a[cnt+1]); 97 for(register int i=1;i<=cnt;i++) { 98 const int x=a[i].x,y=a[i].y,s=a[i].s; 99 t.root[i]=t.modify(t.root[i-1],1,m,x,y,s); 100 } 101 qword ans=0; 102 for(register int i=0;i<q;i++) { 103 int x1=ans%n+1,x2=(ans+getint())%n+1,y1=ans%m+1,y2=(ans+getint())%m+1; 104 if(x1>x2) std::swap(x1,x2); 105 if(y1>y2) std::swap(y1,y2); 106 printf("%llu\n",ans=query(x1-1,y1-1)-query(x1-1,y2)-query(x2,y1-1)+query(x2,y2)); 107 } 108 return 0; 109 }