zoukankan      html  css  js  c++  java
  • 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顾名思义,分别为该位置左,右,上,下点的数量。

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

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

    可以发现,在同一行中,维护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分别为该区间的左,右界.

  • 相关阅读:
    ubuntu 安装redis以及phpredis
    【译】关于Rust模块的清晰解释
    【译】Ringbahn的两个内存Bug
    从背单词到写代码
    【译】Rust中的array、vector和slice
    【译】理解Rust中的闭包
    【译】Rust,无畏并发
    Linux环境下发布.net core
    负载均衡之nginx
    mysql数据库变更监控(canal)
  • 原文地址:https://www.cnblogs.com/soul-M/p/9916467.html
Copyright © 2011-2022 走看看