BZOJ #2874. 训练士兵
description
Ryz正在着手于训练一批精锐士兵
Ryz手下有n*m个士兵,排成一个n行m列的方阵。在一天中,ryz会对士兵下达一些命令,每个命令作用于一个小方阵的所有士兵,并且会增加他们的疲劳值。现在ryz想知道,在一整天训练中,某些小方阵中的士兵的疲劳值总和是多少
Input
第一行有3个整数n,m,k,q。分别表示方阵的行数、列数、指令数和询问数。
接下来k行,每行5个整数x1,x2,y1,y2,s,描述一个指令,表示这条指令对所有坐标(x,y)满足x1<=x<=x2且y1<=y<=y2的士兵产生了s的疲劳值。
(1<=x1<=x2<=n,1<=y1<=y2<=m,0<=s<=1000)
接下来q行,每行2个整数x,y(x,y<=10^9),描述一个询问,询问是被加密的。一个询问的密码是上一个询问的答案(记为c),第一个询问的密码是0。询问参数的计算方式如下:
x1=c % n+1,x2=(c+x) % n+1,如果x1>x2则交换x1和x2
y1=c % m+1,y2=(c+y) % m +1,如果y1>y2则交换y1和y2
询问所有坐标(x,y)满足x1<=x<=x2且y1<=y<=y2的士兵的疲劳值总和。
保证答案<2^64
Output
对于每个询问,输出一行答案
Sample
输入样例1
4 5 3 3
1 3 2 2 7
2 4 2 3 5
1 4 4 5 6
1 2
0 3
2 2
输出样例1
24
12
46
第一次询问的是左上角坐标为(1,1),右下角坐标为(2,3)的这个矩形
第二次询问的是左上角坐标为(1,3),右下角坐标为(1,5)的这个矩形
第三次询问的是左上角坐标为(1,3),右下角坐标为(3,5)这个矩形
输入样例2
5 5 5 5
1 1 1 3 242
1 4 4 5 83
3 5 3 3 221
2 2 1 3 254
5 5 2 2 105
0 1
0 4
2 1
1 3
0 1
输出样例2
484
0
992
442
304
Hint
对于100%的数据 n,m<=10^8,k<=40000,q<=100000;
solution
一个二维矩阵,一般处理套路就是二维差分
对于修改,维护 ( x , y ) (x,y) (x,y)到右下角的贡献
即 ( x 1 , y 1 , x 2 , y 2 ) (x_1,y_1,x_2,y_2) (x1,y1,x2,y2)差分成 ( x 1 , y 1 ) − ( x 1 , y 2 + 1 ) − ( x 2 + 1 , y 1 ) + ( x 2 + 1 , y 2 + 1 ) (x_1,y_1)-(x_1,y_2+1)-(x_2+1,y_1)+(x_2+1,y_2+1) (x1,y1)−(x1,y2+1)−(x2+1,y1)+(x2+1,y2+1)
只用修改四个点
对于询问,则求 ( x , y ) (x,y) (x,y)到左上角的贡献
即 ( x 1 , y 1 , x 2 , y 2 ) (x_1,y_1,x_2,y_2) (x1,y1,x2,y2)差分成 ( x 2 , y 2 ) − ( x 1 , y 2 − 1 ) − ( x 2 − 1 , y 1 ) + ( x 1 − 1 , y 1 − 1 ) (x_2,y_2)-(x_1,y_2-1)-(x_2-1,y_1)+(x_1-1,y_1-1) (x2,y2)−(x1,y2−1)−(x2−1,y1)+(x1−1,y1−1)
只用查询四个点
考虑差分后的某个修改点 ( i , j ) (i,j) (i,j)对于差分后的某个查询点 ( x , y ) (x,y) (x,y)的实际影响
显然 ∀ r o w ∈ [ i , x ] , c o l ∈ [ j , y ] \forall_{row\in[i,x],col\in[j,y]} ∀row∈[i,x],col∈[j,y]的点对 ( r o w , c o l ) (row,col) (row,col)都会加上 s i , j s_{i,j} si,j带来的劳累,贡献和即 ( x − i + 1 ) ( y − j + 1 ) s i , j (x-i+1)(y-j+1)s_{i,j} (x−i+1)(y−j+1)si,j
那么查询一个左上角的
(
1
,
1
)
−
(
x
,
y
)
(1,1)-(x,y)
(1,1)−(x,y)矩阵,这里面每个修改点的劳累贡献都可以计算,求和就是这个查询矩阵的答案
∑
i
=
1
x
∑
j
=
1
y
(
x
−
i
+
1
)
(
y
−
j
+
1
)
s
i
,
j
\sum_{i=1}^x\sum_{j=1}^y(x-i+1)(y-j+1)s_{i,j}
i=1∑xj=1∑y(x−i+1)(y−j+1)si,j
= ∑ i = 1 x ∑ j = 1 y ( x y + y + x + 1 − i y − i − j x − j + i j ) s i , j =\sum_{i=1}^x\sum_{j=1}^y(xy+y+x+1-iy-i-jx-j+ij)s_{i,j} =i=1∑xj=1∑y(xy+y+x+1−iy−i−jx−j+ij)si,j
= ∑ i = 1 x ∑ j = 1 y ( ( x + 1 ) ( y + 1 ) − ( y + 1 ) i − ( x + 1 ) j + i j ) s i , j =\sum_{i=1}^x\sum_{j=1}^y\Big((x+1)(y+1)-(y+1)i-(x+1)j+ij\Big)s_{i,j} =i=1∑xj=1∑y((x+1)(y+1)−(y+1)i−(x+1)j+ij)si,j
= ( x + 1 ) ( y + 1 ) ∑ i = 1 x ∑ j = 1 x s i , j − ( y + 1 ) ∑ i = 1 x ∑ j = 1 x s i , j ∗ i − ( x + 1 ) ∑ i = 1 x ∑ j = 1 x s i , j ∗ j + ∑ i = 1 x ∑ j = 1 x s i , j ∗ i ∗ j =(x+1)(y+1)\sum_{i=1}^x\sum_{j=1}^xs_{i,j}-(y+1)\sum_{i=1}^x\sum_{j=1}^xs_{i,j}*i-(x+1)\sum_{i=1}^x\sum_{j=1}^xs_{i,j}*j+\sum_{i=1}^x\sum_{j=1}^xs_{i,j}*i*j =(x+1)(y+1)i=1∑xj=1∑xsi,j−(y+1)i=1∑xj=1∑xsi,j∗i−(x+1)i=1∑xj=1∑xsi,j∗j+i=1∑xj=1∑xsi,j∗i∗j
对修改的 ( i , j , s i , j ) (i,j,s_{i,j}) (i,j,si,j)分别维护四个值即可
剩下的就是二维主席树了,将二维坐标离散化即可
code
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define maxn 200000
vector < pair < int, int > > G[maxn];
struct node {
int lson, rson, o, i, j, ij;
node(){}
node( int x, int y, int w ) {
o = w, i = x * w, j = y * w, ij = x * y * w;
}
void operator += ( node &New ) {
o += New.o, i += New.i, j += New.j, ij += New.ij;
}
}t[maxn * 30];
struct Node { int x, y, w; }opt[maxn];
int n, m, k, Q, tot, cnt, cnt_x, cnt_y;
int root[maxn], X[maxn], Y[maxn];
void modify( int &now, int lst, int l, int r, int pos, node New ) {
t[now = ++ cnt] = t[lst];
t[now] += New;
if( l == r ) return;
int mid = ( l + r ) >> 1;
if( pos <= mid ) modify( t[now].lson, t[lst].lson, l, mid, pos, New );
else modify( t[now].rson, t[lst].rson, mid + 1, r, pos, New );
}
int query_o( int now, int l, int r, int pos ) {
if( ! now or pos < l ) return 0;
if( r <= pos ) return t[now].o;
int mid = ( l + r ) >> 1;
return query_o( t[now].lson, l, mid, pos ) + query_o( t[now].rson, mid + 1, r, pos );
}
int query_i( int now, int l, int r, int pos ) {
if( ! now or pos < l ) return 0;
if( r <= pos ) return t[now].i;
int mid = ( l + r ) >> 1;
return query_i( t[now].lson, l, mid, pos ) + query_i( t[now].rson, mid + 1, r, pos );
}
int query_j( int now, int l, int r, int pos ) {
if( ! now or pos < l ) return 0;
if( r <= pos ) return t[now].j;
int mid = ( l + r ) >> 1;
return query_j( t[now].lson, l, mid, pos ) + query_j( t[now].rson, mid + 1, r, pos );
}
int query_ij( int now, int l, int r, int pos ) {
if( ! now or pos < l ) return 0;
if( r <= pos ) return t[now].ij;
int mid = ( l + r ) >> 1;
return query_ij( t[now].lson, l, mid, pos ) + query_ij( t[now].rson, mid + 1, r, pos );
}
int query( int x, int y ) {
if( ! x or ! y ) return 0;
int i = upper_bound( X + 1, X + cnt_x + 1, x ) - X - 1;
int j = upper_bound( Y + 1, Y + cnt_y + 1, y ) - Y - 1;
int s1 = ( x + 1 ) * ( y + 1 ) * query_o( root[i], 1, cnt_y, j );
int s2 = ( y + 1 ) * query_i( root[i], 1, cnt_y, j );
int s3 = ( x + 1 ) * query_j( root[i], 1, cnt_y, j );
int s4 = query_ij( root[i], 1, cnt_y, j );
return s1 - s2 - s3 + s4;
}
signed main() {
scanf( "%lld %lld %lld %lld", &n, &m, &k, &Q );
for( int i = 1, x1, x2, y1, y2, s;i <= k;i ++ ) {
scanf( "%lld %lld %lld %lld %lld", &x1, &x2, &y1, &y2, &s );
opt[++ tot] = { x1, y1, s };
opt[++ tot] = { x1, y2 + 1, -s };
opt[++ tot] = { x2 + 1, y1, -s };
opt[++ tot] = { x2 + 1, y2 + 1, s };
X[++ cnt_x] = x1, X[++ cnt_x] = x2 + 1;
Y[++ cnt_y] = y1, Y[++ cnt_y] = y2 + 1;
}
sort( X + 1, X + cnt_x + 1 );
cnt_x = unique( X + 1, X + cnt_x + 1 ) - X - 1;
sort( Y + 1, Y + cnt_y + 1 );
cnt_y = unique( Y + 1, Y + cnt_y + 1 ) - Y - 1;
for( int i = 1;i <= tot;i ++ ) {
int x = lower_bound( X + 1, X + cnt_x + 1, opt[i].x ) - X;
int y = lower_bound( Y + 1, Y + cnt_y + 1, opt[i].y ) - Y;
G[x].push_back( make_pair( y, i ) );
}
for( int i = 1;i <= cnt_x;i ++ ) {
root[i] = root[i - 1];
for( auto E : G[i] ) {
int j = E.first, id = E.second;
modify( root[i], root[i], 1, cnt_y, j, node( opt[id].x, opt[id].y, opt[id].w ) );
}
}
int ans = 0, x1, y1, x2, y2, x, y;
while( Q -- ) {
scanf( "%lld %lld", &x, &y );
x1 = ans % n + 1, x2 = ( ans + x ) % n + 1;
if( x1 > x2 ) swap( x1, x2 );
y1 = ans % m + 1, y2 = ( ans + y ) % m + 1;
if( y1 > y2 ) swap( y1, y2 );
printf( "%lld\n", ans = query( x2, y2 ) - query( x2, y1 - 1 ) - query( x1 - 1, y2 ) + query( x1 - 1, y1 - 1 ) );
}
return 0;
}