BZOJ #2874. 训练士兵(差分+离散化+主席树)

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,y21)(x21,y1)+(x11,y11)

只用查询四个点

考虑差分后的某个修改点 ( 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} (xi+1)(yj+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=1xj=1y(xi+1)(yj+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=1xj=1y(xy+y+x+1iyijxj+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=1xj=1y((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=1xj=1xsi,j(y+1)i=1xj=1xsi,ji(x+1)i=1xj=1xsi,jj+i=1xj=1xsi,jij

对修改的 ( 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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值