Orz在前面
本来是刷数据结构的题的,然后在hzwer的数据结构里看到这道题…
然后3个小时就没有了…
看题解看不懂,又去看别人代码,然后才…= =唉
但是这道题和BZOJ1818有相似之处,思想十分巧妙,这道题维护的是数据的前缀和,而1818则是将直线拆成端点用差分,感觉收获不小
题目(复制自BZOJ)
题目描述
小W 是一片新造公墓的管理人。公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地。当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地。为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度。一块墓地的虔诚度是指以这块墓地为中心的十字架的数目。一个十字架可以看成中间是墓地,墓地的正上、正下、正左、正右都有恰好k 棵常青树。小W 希望知道他所管理的这片公墓中所有墓地的虔诚度总和是多少
输入
第一行包含两个用空格分隔的正整数N 和M,表示公墓的宽和长,因此这个矩形公墓共有(N+1) ×(M+1)个格点,左下角的坐标为(0, 0),右上角的坐标为(N, M)。第二行包含一个正整数W,表示公墓中常青树的个数。第三行起共W 行,每行包含两个用空格分隔的非负整数xi和yi,表示一棵常青树的坐标。输入保证没有两棵常青树拥有相同的坐标。最后一行包含一个正整数k,意义如题目所示。
输出
包含一个非负整数,表示这片公墓中所有墓地的虔诚度总和。为了方便起见,答案对2,147,483,648 取模。
样例数据
Sample Input 5 6
13
0 2
0 3
1 2
1 3
2 0
2 1
2 4
2 5
2 6
3 2
3 3
4 3
5 2
2
Sample output
6
题解
根据题意,要求的就是每个墓碑位置上的形成十字架的总数,这个总数可以用组合数来算。
数据规模很大,N,M都是1e9级别,但是树的数目W却只有1e5级别,这将导致很多墓碑四周都没有树,那么它的虔诚度为0,即对答案贡献为0。那么很明显对于这样的数据我们可以进行离散化。
下面的步骤都是建立在已经对所有点的横坐标离散化之后的基础上。
我们分别维护四个数组, L[i] , R[i] , U[i] , D[i] 分别代表,每个点左边(Left),右边(Right),上面(Up),下面(Down)分别有多少棵树,然后N方暴力枚举,求和即为答案。
= =但是是不能这样做的,N^2大暴力不TLE才怪呢,空间也不够
我们发现,对于两棵在同一横排的树(纵坐标相等),设为A树和B树。那么对于这两棵树中间的墓,它们的L[i]和R[i]都是一样的。利用这个性质,我们可以想办法一次性求一段区间的答案,这样就可以避免计算不必要的L[i]和R[i]
我们先将所有树以y为第一关键字x为第二关键字从小到大排序,这样就可以在O(n)时间内统计出对于该树的L,R,U,D(L表示,在它到它右边下一棵树的这段开区间里的点的L[i],类比R,U,D)
使用树状数组维护横行的前缀和,前缀和相减得到区间。
从下往上扫,考虑当前扫描到了某一高度,但是当这根线继续向上走的时候,原来被统计进入U的点,现在被统计进入了D。也就是说,每扫到一个点,就需要更新该点所在列的数据,现在的是C(U,k)*C(D,K),原来的是C(U+1,k)*C(D-1,k),也就是说,每次需要把当前点所在列的值更新为C(U,k)*C(D,K)-C(U+1,k)*C(D-1,k)。
主要操作就是以上,剩下的就是细节问题了= =
自带大常数的代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int ans , N , K , uninum , cnt[100005] , c[100005][11] ;
struct Point{
int x , y , L , R , U , D ;
bool operator < ( const Point &A ) const{
return y < A.y || ( y == A.y && x < A.x ) ;
}
}p[100005] ;
struct Uniq{
int x , id ;
bool operator < ( const Uniq &A ) const{
return x < A.x ;
}
}U[100005] ;
struct BIT{
int b[100005] ;
void clear(){
memset( b , 0 , sizeof( b ) ) ;
}
void Modify( int x , int d ){
// printf( "Modify start :: x = %d d = %d\n" , x , d ) ;
for( ; x <= uninum ; x += x&(-x) )
b[x] += d ;
// printf( "Modify ended\n" ) ;
}
int Query( int x ){
if( x == 0 ) return 0 ;
// printf( "Query start :: x = %d\n" , x ) ;
int rt = 0 ;
for( ; x ; x -= x&(-x) ) rt += b[x] ;
// printf( "Query ended with :: %d\n" , rt ) ;
return rt ;
}
}B ;
void solve(){
c[0][0] = 1 ;
for( int i = 1 ; i <= N ; i ++ ){
c[i][0] = 1 ;
for( int j = 1 ; j <= i && j <= K ; j ++ )
c[i][j] = c[i-1][j] + c[i-1][j-1] ;
}
sort( p + 1 , p + N + 1 ) ;
for( int i = 1 , tmp = 0 ; i <= N ; i ++ ){
tmp = ( p[i-1].y == p[i].y ? tmp + 1 : 1 ) ;
cnt[ p[i].x ] ++ ;
p[i].L = tmp ; p[i].D = cnt[ p[i].x ] ;
}
for( int i = N , tmp = 0 ; i >= 1 ; i -- ){
tmp = ( p[i].y == p[i+1].y ? tmp + 1 : 1 ) ;
p[i].R = tmp ; p[i].U = cnt[ p[i].x ] - p[i].D ;
}
B.clear() ;
for( int i = 1 ; i <= N ; i ++ ){
//printf( "M Q :: i = %d\n" , i ) ;
B.Modify( p[i].x , c[ p[i].U ][K] * c[ p[i].D ][K] - B.Query( p[i].x ) + B.Query( p[i].x - 1 ) ) ;
if( p[i-1].y == p[i].y )
ans += c[ p[i-1].L ][K] * c[ p[i].R ][K] * ( B.Query( p[i].x - 1 ) - B.Query( p[i-1].x ) ) ;
}
printf( "%d" , ans & 2147483647 ) ;
}
int main(){
scanf( "%*d%*d%d" , &N ) ;
for( int i = 1 ; i <= N ; i ++ ){
scanf( "%d%d" , &p[i].x , &p[i].y ) ;
U[i].id = i ; U[i].x = p[i].x ;
} scanf( "%d" , &K ) ;
sort( U + 1 , U + N + 1 ) ;
U[0].x = -1 ;
for( int i = 1 ; i <= N ; i ++ ){
if( U[i-1].x != U[i].x ) uninum ++ ;
p[ U[i].id ].x = uninum ;
//printf( "p[%d].x = %d\n" , U[i].id , p[ U[i].id ].x ) ;
}
solve() ;
}