Luogu P2154 [SDOI2009]虔诚的墓主人

一道算是数学题的题吧。本人调了一天。

题目大意:

给定一个直角坐标系,其中有一些点.

如果坐标系中的某个位置满足:

①该位置没有点

②其正上方,正下方,正左方,正右方均有不少于k个点,

那么就可以以该位置为中心,从四个方向中各任选k个点,组成一个十字架.

求:整个图上的十字架的数量。

注意图的大小1≤N, M≤1,000,000,000,且1的数量1≤W≤100,000,1≤k≤10;

题解:

首先可以发现:异常巨大的图,但点的数量并不多。

此时就需要将数据离散化.

比如我有以下一些点:

$ (1,2) $
$ (50,2) $
$ (1,999999) $
$ (8848,8848) $

那么就按照大小关系,将其离散化为

$ (1,1) $
$ (2,1) $
$ (1,3) $
$ (3,2) $

并不会产生影响。

然后将点排序以确保接下来的遍历顺序:以y为第一关键字,x为第二关键字排序所有的点。

这里我们考虑:对于一个位置,以其为中心产生的十字架的数量为 $ C(left,k)C(right,k)C(up,k)*C(down,k) $

其中left,right,up,down顾名思义,分别为该位置左,右,上,下点的数量。

如图:蓝点表示位置,绿点代表点。

1477449-20181106173700796-275067566.png

我们考虑按照刚刚排的顺序枚举每个点(实际上是这个点与其左边的点形成的区间中)答案的数量。

可以发现,在同一行中,维护left和right是相对容易的:每到一个新的点,就将left+1,而right即为该行点的个数减去left.

一段区间之内,left和right是不变的,但up与down却不尽相同。

根据乘法分配律可以得到,该段中的答案即为 $ C(left,k)×C(right,k) $ , 乘上该区间的 $ C(up,k)×C(down,k) $ 的和.

问题是如何快速得知某段区间内 $ C(up,k)*C(down,k) $ 的和.

我们发现,这个值只需要在每到一个新的点时,将该点对应y坐标的 $ C(up,k)×C(down,k) $ 的值改为 $ C(up-1,k)×C(down+1,k) $ 就可以了.

单点修改,区间查询,就可以用树状数组来维护.

具体实现:

维护一个数组cnt,cnt[x]用于记录横坐标为x的点已经被遍历过(在当前行下面)的数量.

每次遍历到一个点时:

设其坐标为 $ (x,y) $ .cnt[x]+1,将树状数组的第 $ x $ 位修改为 $ C(cnt[x][k]×C[total[x]-cnt[x]][k]) $

·如果该点是某一行的第一个点,直接continue到下一个点,因为不可能产生贡献。

否则:该区间(当前点和上个点形成的区间)内的十字架中心个数为:

$ C[left][k]×C[right][k]×sum(l,r) $ ,其中l,r分别为该区间的左,右界.

转载于:https://www.cnblogs.com/soul-M/p/9916467.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值