说在前面
有点时间没有写这种矩形差分的题了,忘记了较小的边界要-1…WA了几次
然后就开始各种优化,然而还是进不了第一页=A=
另外,第一次单日提交满50次纪念!
(纪念个MMP啊= =,劳资还想交题呢)
题目
题面
维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.
输入输出格式
输入格式:
第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小
接下来每行为一下三种输入之一(不包含引号):
“1 x y a”
“2 x1 y1 x2 y2”
“3”
输入1:你需要把(x,y)(第x行第y列)的格子权值增加a
输入2:你需要求出以左下角为(x1,y1),右上角为(x2,y2)的矩阵内所有格子的权值和,并输出
输入3:表示输入结束
输出格式:
对于每一个询问,输出一个数字表示答案,答案保证在int范围内
解法
推荐这篇,写得比较详细
传送门 for BraketBN’s blog
单独看每个询问,我们可以把每个对于矩形的询问,拆成四个对于前缀和的询问(前缀和差分思想)
可以发现,对拆分之后的询问有贡献的修改操作,一定是时间在它之前的,并且x,y坐标都要小于等于它的,很明显是一个三维偏序,然后CDQ就好了= =
具体来说:x纬度用sort,时间纬度在二分保证,y纬度使用树状数组查询前缀和
下面是自带大常数的代码
实现的时候,me把y坐标离散化了,这样可以使树状数组那个log变小很多
不离散化也可以过,大概是7秒左右
/**************************************************************
Problem: 1176
User: Izumihanako
Language: C++
Result: Accepted
Time:2712 ms
Memory:32232 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int S , W , topp , cnta , uninum ;
int ans[10005] ;
struct Options{
int x , y , delta , tim , id , opt ;
Options(){} ;
Options( int x_ , int y_ , int delta_ , int tim_ , int id_ , int opt_ ) :
x(x_) , y(y_) , delta(delta_) , tim(tim_) , id(id_) , opt(opt_){} ;
bool operator < ( const Options &A ) const {
if( x == A.x && y == A.y ) return opt < A.opt ;
if( x == A.x ) return y < A.y ;
return x < A.x ;
}
}o[640005] , tmpL[320005] , tmpR[320005] ;
struct BIT{
int b[350000] ;
void add( int x , int delta ){
for( ; x <= uninum ; x += x&-x )
b[x] += delta ;
}
int Query( int x ){
int rt = 0 ;
for( ; x ; x -= x&-x )
rt += b[x] ;
return rt ;
}
}B;
inline bool cmpy( const Options &A , const Options &B ) {
return A.y < B.y ;
}
void CDQ( int lf , int rg ){
if( lf == rg ) return ;
int Rcnt = 0 , Lcnt = 0 , mid = ( lf + rg ) >> 1 ;
for( int i = lf ; i <= rg ; i ++ ){
if( o[i].tim <= mid && o[i].opt == 1 ) B.add( o[i].y , o[i].delta ) ;
if( o[i].tim > mid && o[i].opt == 2 ) ans[ o[i].id ] += B.Query( o[i].y ) * o[i].delta ;
}
for( int i = lf ; i <= rg ; i ++ ){
if( o[i].tim <= mid && o[i].opt == 1 ) B.add( o[i].y , -o[i].delta ) ;
if( o[i].tim <= mid ) tmpL[++Lcnt] = o[i] ;
if( o[i].tim > mid ) tmpR[++Rcnt] = o[i] ;
}
for( int i = 1 ; i <= Lcnt ; i ++ ) o[lf+i-1] = tmpL[i] ;
for( int i = 1 ; i <= Rcnt ; i ++ ) o[lf+Lcnt-1+i] = tmpR[i] ;
CDQ( lf , mid ) ; CDQ( mid+1 , rg ) ;
}
void solve(){
sort( o + 1 , o + topp + 1 , cmpy ) ;
for( register int i = 1 , last = -1 ; i <= topp ;i ++ ){
if( last != o[i].y ) uninum ++ , last = o[i].y ;
o[i].y = uninum ;
}
sort( o + 1 , o + topp + 1 ) ;
for( register int i = 1 ; i <= W ; i ++ )
B.add( i , S ) ;
CDQ( 1 , topp ) ;
for( int i = 1 ; i <= cnta ; i ++ )
printf( "%d\n" , ans[i] ) ;
}
inline int read_(){
char ch = getchar() ;
int rt = 0 , flag = 1 ;
while( ch < '0' || ch > '9' ){
if( ch == '-' ) flag = -1 ;
ch = getchar() ;
}
while( ch >='0' && ch <='9' ) rt = rt * 10 + ch - 48 , ch = getchar() ;
return rt * flag ;
}
int main(){
scanf( "%d%d" , &S , &W ) ;
for( register int a , b , c , d , delta , opt ; ; ){
scanf( "%d" , &opt ) ;
if( opt == 1 ){
a = read_() , b = read_() , delta = read_() ;
o[++topp] = (Options){ a , b , delta , topp , 0 , 1 } ;
} else if( opt == 2 ){
++ cnta ;
a = read_() , b = read_() , c = read_() , d = read_() ;
o[++topp] = (Options){ a-1 , b-1 , 1 , topp , cnta , 2 } ;
o[++topp] = (Options){ a-1 , d , -1 , topp , cnta , 2 } ;
o[++topp] = (Options){ c , b-1 , -1 , topp , cnta , 2 } ;
o[++topp] = (Options){ c , d , 1 , topp , cnta , 2 } ;
} else break ;
}
solve() ;
}
/*
0 4
1 2 3 3
2 1 1 3 3
3
*/