BZOJ 1227 [SDOI2009] 忠诚的墓主人 离线+树状数组+离散化
BZOJ 1227 [SDOI2009] 虔诚的墓主人 离线+树状数组+离散化
因为现在树状数组存的是
而我们要将之更新成
所以说,树状数组的作用就是维护这段区间的
鸣谢:140142耐心讲解缕清了我的思路
题意:由于调这道题调的头昏脑涨,所以题意自己搜吧,懒得说。
方法:离线+树状数组+离散化
解析:首先深表本蒟蒻对出题人的敬(bi)意(shi)。这道题简直丧心病狂,看完题后大脑一片空白,整个人都不好了,刚开始的思路是什么呢?暴力思想枚举每个墓碑,然后计算每个墓碑的虔诚度,然后再来统计。不过看看数据范围呢?10^9*10^9的矩阵,最多才10^5个树,光枚举就已经超时了,所以肯定不行。(不过要是考试真没思路我就那么搞了- -!)
然后也想到来枚举墓碑,不过还是些许犹豫,毕竟偏差较大,写的话实现难。然后我就不知道怎么搞了。
今天140142神犇耐心的给我讲了大体的思路。
就是呢,把所有的点按x从大到小,x相同y从大到小排序,然后呢一个个来枚举。
用如上的图来说。
先明确树状数组在本题是如何体现的。
首先这个树状数组存的值是什么?
c[u[i]][K]∗c[d[i]][K] 其中,c是组合数,u[i]是对于i这个总坐标,当前的上面的树的个数,d代表向下求。
现在这些点的顺序应该已经排好了,那么我来模拟一下正解的过程。
显然第一个遍历的点应该是[0,3]。
这时候我们能做什么呢? 统计他的左边有多少点,右边有多少点,当然,把他归到左边。为什么?因为我们是从左到右处理,所以后面假设这一排还有点的话,[0,3]这个点当然在左边。
统计完左右后,我们还能维护当前的上下,道理差不多吧。
经过很多的实验,个人认为统计这些点上下左右最好用map来搞,非常实用。
以上就是第一个遍历过程。
接下来,到[1,3]这个点,此时呢,这个点又到了新的一排,所以左右的话应该重新维护一下,而上下呢?显然只是上–,下++。但是这时候就要注意了。在对上下进行操作之前,当前的树状数组显然存的是[0,3]时候的值,所以我们要进行更新。而怎么更新呢?
因为现在树状数组存的是c[u[3]][K]∗c[d[3]][K] ,
而我们要将之更新成c[u[3]−1][K]∗c[d[3]+1][K] ,也就是对应着上–,下++。为什么要这么做呢?
让我们接着遍历。
[3,0]这个点同样换行了,所以维护左右,上下也填到树状数组。
[3,1]这个店没有换行,所以左++,右–,上下填到树状数组。
**
关键就是到了[3,5]这个点。
**
我们可以看出,此时他与[3,1]之间是有3个点满足题意的。那我们就要把这三个点的值加到答案上(其实[3,1]时也有这个过程,只不过它与上一个点紧挨着,所以不可能有解,被我跳过了)而这个加的值应该是什么?
(∑i=24c[u[i]][K]∗c[d[i]][K])∗c[l[5−1]][K]∗c[r[5−1]][K]
所以说,树状数组的作用就是维护这段区间的c[u[i]][k]∗c[d[i]][k] 的和的,只不过是多了动态更新,也就是每个点过后,都要重新更新树状数组的值。
树状数组的用法就说完了,如果没懂,亦或是看看别人的题解,亦或是请教请教AC的人,亦或是理解理解上面这个求和公式。
接下来
图中我特意留了几个空行,我们发现,2,4,7这三列并没有什么卵用,完全可以删除掉,不过这个删除是必须的,因为10^9的边长的矩形,咱们只算其中10^5个点,光是内存就开不下,所以离散化一下列,也就是纵坐标是必然的。
后记:我太弱
#include <stdio.h>
#include <map>
#include <algorithm>
#define N 100100
#define mod 2147483648ll
typedef long long ll ;
using namespace std ;
struct node
{
int x ;
int y ;
};
node a[N] ;
int n , m , K;
int w , tot;
map <int , int> M ;
map <int , int> line ;
map <int , int> line_x ;
ll c[N][11] ;
int yy[N] ;
int f[N] ;
int u[N] ;
ll sum[N] ;
int d[N] ;
int lowbit(int x)
{
return x & (-x) ;
}
void update(int x , ll p)
{
while(x <= tot)
{
sum[x] = (sum[x] + p)% mod ;
x += lowbit(x) ;
}
}
ll getsum(int x)
{
ll ret = 0 ;
while(x)
{
ret = (ret + sum[x])%mod ;
x -= lowbit(x) ;
}
return ret ;
}
void init()
{
c[0][0] = 1 ;
for(int i = 1 ; i <= w ; i++)
{
c[i][0] = 1 ;
int tmp = min(i , K) ;
for(int j = 1 ; j <= tmp ; j++)
{
c[i][j] = (c[i-1][j] + c[i-1][j-1])%mod ;
}
}
}
bool cmp(node a , node b)
{
if(a.x == b.x) return a.y < b.y ;
return a.x < b.x ;
}
ll ans ;
int main()
{
scanf("%d%d" , &n , &m) ;
scanf("%d" , &w) ;
for(int i = 1 ; i <= w ; i++)
{
scanf("%d%d" , &a[i].x , &a[i].y) ;
yy[i] = a[i].y ;
line[a[i].y] ++ ;
line_x[a[i].x] ++ ;
}
scanf("%d" , &K) ;
init() ;
sort(a + 1 , a + w + 1 , cmp) ;
sort(yy + 1 , yy + w + 1) ;
yy[0] = -1 ;
for(int i = 1 ; i <= w ; i++)
{
if(yy[i] != yy[i-1])
{
M[yy[i]] = ++tot ;
f[tot] = yy[i] ;
}
}
for(int i = 1 ; i <= tot ; i++)
{
u[i] = line[f[i]] ;
d[i] = 0 ;
}
int tmp_line = -1 ;
int l , r ;
for(int i = 1 ; i <= w ; i++)
{
if(a[i].x != tmp_line)
{
tmp_line = a[i].x ;
l = 1 , r = line_x[a[i].x] - 1 ;
int ori = M[a[i].y] ;
update(ori , (c[u[ori]-1][K]*c[d[ori]+1][K]%mod-c[u[ori]][K]*c[d[ori]][K]%mod)%mod) ;
u[ori]-- ;
d[ori]++ ;
}else
{
ll S = getsum(M[a[i].y]-1) - getsum(M[a[i-1].y]) ;
if(S < 0) S += mod ;
ans = (ans + ((S*c[l][K])%mod * c[r][K])%mod)%mod ;
l ++ , r-- ;
int ori = M[a[i].y] ;
update(ori , (c[u[ori]-1][K]*c[d[ori]+1][K]%mod-c[u[ori]][K]*c[d[ori]][K]%mod)%mod) ;
u[ori] -- ;
d[ori]++ ;
}
}
printf("%lld\n" , ans) ;
}