题目大意

  公墓可以看成一块N×M的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地。一块墓地的虔诚度是指以这块墓地为中心的十字架的数目,一个十字架可以看成中间是墓地,墓地的正上、正下、正左、正右都有恰好k棵常青树。小W希望知道他所管理的这片公墓中所有墓地的虔诚度总和是多少。1 ≤ N, M ≤ 1,000,000,000,0 ≤ xi ≤ N,0 ≤ yi ≤ M,1 ≤ W ≤ 100,000,1 ≤ k ≤ 10。

题解

  首先,由N、M的巨大数量和相对地W的较小数量让我们想到离散化。一个点若其所在排和列都没有常青树,则这些点都是无效的。所以我们可以通过离散化将原矩形中的无效部分删去得到一个小矩形。

  然而离散化后我们不可以枚举坐标,这样时间复杂度至少是O(W^2)。所以我们就要以两个点之间的间隔上做文章。

  对于一个点$i$,定义它们上下左右方的常青树(包括它自己)个数分别为$u_i,b_i,l_i,r_i$,它对答案的贡献为$C_{u_i}^k C_{b_i}^k C_{l_i}^k C_{r_i}^k$。对于一排上相邻的两个点$a,b$,它们之间的间隔上每一个点的$C_{l_i}^k C_{r_i}^k$都相等,它们对答案的贡献是$C_{l_a}^k C_{r_a}^k \sum_{i在a,b之间}C_{u_i}^k C_{b_i}^k$。看见sigma了,是不是可以想到树状数组?当处理当前排时,树状数组的key值为列数(也就是架在这一行上),维护的是该行上每一个点的$C_{u_i}^k C_{b_i}^k$的前缀和。这样一行一行地按照列数从小到大(排一下序)扫描常青树,边查询边修改树状数组,这道题就完成了。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5.  
  6. const int MAX_NODE = 100010, MAX_CANDIDATE_VAL = MAX_NODE * 2, MAX_K = 15;
  7. const unsigned int P = 2147483648u;
  8. int TotRow, TotCol, TotNode, K;
  9. int RowNodeCnt[MAX_CANDIDATE_VAL], ColNodeCnt[MAX_CANDIDATE_VAL];
  10. unsigned int C[MAX_NODE][MAX_K];
  11.  
  12. struct Discretion
  13. {
  14. private:
  15. int OrgData[MAX_CANDIDATE_VAL], Rank[MAX_CANDIDATE_VAL];
  16. int N;
  17.  
  18. int LowerBound(int l, int r, int k)
  19. {
  20. while (l < r)
  21. {
  22. int mid = (l + r) / 2;
  23. if (k <= OrgData[mid])
  24. r = mid;
  25. else
  26. l = mid + 1;
  27. }
  28. return l;
  29. }
  30.  
  31. public:
  32. Discretion(int n, int *orgData)
  33. {
  34. N = n;
  35. for (int i = 1; i <= N; i++)
  36. OrgData[i] = orgData[i];
  37. sort(OrgData + 1, OrgData + N + 1);
  38. OrgData[0] = -1;
  39. int curRank = 0;
  40. for (int i = 1; i <= N; i++)
  41. Rank[i] = OrgData[i] == OrgData[i - 1] ? curRank : ++curRank;
  42. }
  43.  
  44. int GetRank(int val)
  45. {
  46. return Rank[LowerBound(1, N, val)];
  47. }
  48.  
  49. int GetMaxRank()
  50. {
  51. return Rank[N];
  52. }
  53. };
  54.  
  55. struct Node
  56. {
  57. int Row, Col;
  58.  
  59. bool operator < (const Node& a) const
  60. {
  61. return Row != a.Row ? Row < a.Row : Col < a.Col;
  62. }
  63. }_nodes[MAX_NODE];
  64.  
  65. struct BIT
  66. {
  67. private:
  68. unsigned int C[MAX_CANDIDATE_VAL];
  69. int N;
  70.  
  71. int Lowbit(int x)
  72. {
  73. return x & -x;
  74. }
  75.  
  76. public:
  77. BIT(int n):N(n){}
  78.  
  79. void Update(int p, unsigned int delta, bool add)
  80. {
  81. while (p <= N)
  82. {
  83. add ? C[p] += delta : C[p] -= delta;
  84. p += Lowbit(p);
  85. }
  86. }
  87.  
  88. unsigned int Query(int p)
  89. {
  90. unsigned int ans = 0;
  91. while (p >= 1)
  92. {
  93. ans += C[p];
  94. p -= Lowbit(p);
  95. }
  96. return ans;
  97. }
  98. };
  99.  
  100. void Read()
  101. {
  102. scanf("%d%d%d", &TotRow, &TotCol, &TotNode);
  103. for (int i = 1; i <= TotNode; i++)
  104. scanf("%d%d", &_nodes[i].Row, &_nodes[i].Col);
  105. scanf("%d", &K);
  106. }
  107.  
  108. void GetC()
  109. {
  110. for (int i = 0; i <= TotNode; i++)
  111. {
  112. C[i][0] = 1;
  113. if (i <= K)
  114. C[i][i] = 1;
  115. for (int j = 1; j <= min(i - 1, K); j++)
  116. C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
  117. }
  118. }
  119.  
  120. void Discrete()
  121. {
  122. static int a[MAX_CANDIDATE_VAL];
  123. for (int i = 1; i <= TotNode; i++)
  124. {
  125. a[i * 2 - 1] = _nodes[i].Row;
  126. a[i * 2] = _nodes[i].Col;
  127. }
  128. static Discretion g(TotNode * 2, a);
  129. TotRow = TotCol = g.GetMaxRank();
  130. for (int i = 1; i <= TotNode; i++)
  131. {
  132. _nodes[i].Row = g.GetRank(_nodes[i].Row);
  133. _nodes[i].Col = g.GetRank(_nodes[i].Col);
  134. RowNodeCnt[_nodes[i].Row]++;
  135. ColNodeCnt[_nodes[i].Col]++;
  136. }
  137. }
  138.  
  139. int GetAns()
  140. {
  141. static BIT g(TotCol);
  142. unsigned int ans = 0;
  143. sort(_nodes + 1, _nodes + TotNode + 1);
  144. int pass = 0;//passNodeCntInCurRow
  145. static int colPassCnt[MAX_CANDIDATE_VAL];
  146. for (int i = 1; i < TotNode; i++)
  147. {
  148. pass = _nodes[i - 1].Row == _nodes[i].Row ? pass + 1 : 1;
  149. if (_nodes[i + 1].Row == _nodes[i].Row)
  150. ans += C[pass][K] * C[RowNodeCnt[_nodes[i].Row] - pass][K] * (g.Query(_nodes[i + 1].Col - 1) - g.Query(_nodes[i].Col));
  151. g.Update(_nodes[i].Col, g.Query(_nodes[i].Col) - g.Query(_nodes[i].Col - 1), false);
  152. colPassCnt[_nodes[i].Col]++;
  153. g.Update(_nodes[i].Col, C[colPassCnt[_nodes[i].Col]][K] * C[ColNodeCnt[_nodes[i].Col] - colPassCnt[_nodes[i].Col]][K], true);
  154. }
  155. return (int)(ans % P);
  156. }
  157.  
  158. int main()
  159. {
  160. Read();
  161. GetC();
  162. Discrete();
  163. printf("%d\n", GetAns());
  164. return 0;
  165. }

  

luogu2154 [SDOI2009] 虔诚的墓主人 离散化 树状数组 扫描线的更多相关文章

  1. [BZOJ1227][SDOI2009]虔诚的墓主人 组合数+树状数组

    1227: [SDOI2009]虔诚的墓主人 Time Limit: 5 Sec  Memory Limit: 259 MBSubmit: 1433  Solved: 672[Submit][Stat ...

  2. BZOJ1227 SDOI2009 虔诚的墓主人【树状数组+组合数】【好题】*

    BZOJ1227 SDOI2009 虔诚的墓主人 Description 小W 是一片新造公墓的管理人.公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地. ...

  3. BZOJ1227 [SDOI2009]虔诚的墓主人 【树状数组】

    题目 小W 是一片新造公墓的管理人.公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地.当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地.为 ...

  4. BZOJ 1227 虔诚的墓主人(离散化+树状数组)

    题目中矩形的尺寸太大,导致墓地的数目太多,如果我们统计每一个墓地的虔诚度,超时是一定的. 而常青树的数目<=1e5.这启发我们从树的方向去思考. 考虑一行没有树的情况,显然这一行的墓地的虔诚度之 ...

  5. bzoj1227: [SDOI2009]虔诚的墓主人(树状数组,组合数)

    传送门 首先,对于每一块墓地,如果上下左右各有$a,b,c,d$棵树,那么总的虔诚度就是$C_k^a*C_k^b*C_k^c*C_k^d$ 那么我们先把所有的点都给离散,然后按$x$为第一关键字,$y ...

  6. 【Luogu】P2154虔诚的墓主人(树状数组)

    题目链接 这题就是考虑我们有这样一个情况

  7. CodeForces 540E - Infinite Inversions(离散化+树状数组)

    花了近5个小时,改的乱七八糟,终于A了. 一个无限数列,1,2,3,4,...,n....,给n个数对<i,j>把数列的i,j两个元素做交换.求交换后数列的逆序对数. 很容易想到离散化+树 ...

  8. Ultra-QuickSort(归并排序+离散化树状数组)

    Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 50517   Accepted: 18534 ...

  9. HDU 5862 Counting Intersections(离散化+树状数组)

    HDU 5862 Counting Intersections(离散化+树状数组) 题目链接http://acm.split.hdu.edu.cn/showproblem.php?pid=5862 D ...

随机推荐

  1. (转)Vuex简单入门

    今天试了一下Vuex,感觉跟Redux的实现思想类似.再此,简单地总结一下. 什么是Vuex 在Vue中,多组件的开发给我们带来了很多的方便,但同时当项目规模变大的时候,多个组件间的数据通信和状态管理 ...

  2. wampserver修改mysql数据库密码的简单方式

    刚装好的wampserver的数据库是没有密码的,所以可以直接登录,要设置密码,一种简单的方式如下: 打开phpMyadmin 初始状态没有密码,可以直接登录 登录之后,点击账户 点击修改权限,设置你 ...

  3. 简单TCP代码

    服务器: SOCKET s; s = ::socket(AF_INET,SOCK_STREAM,); sockaddr_in addr; addr.sin_family = AF_INET; addr ...

  4. 离线安装Selenium

    https://blog.csdn.net/poem_ruru/article/details/79032140

  5. 【转】SSH中 整合spring和proxool 连接池

    [摘要:比来做的一个项目中应用到了毗邻池技巧,大概我们人人比拟认识的开源毗邻池有dbcp,c3p0,proxool.对那三种毗邻池来讲,从机能战失足率来讲,proxool轻微比前两种好些.本日我首要简 ...

  6. BZOJ 2096: [Poi2010]Pilots 单调队列

    Code: #include<bits/stdc++.h> #define maxn 4000000 using namespace std; void setIO(string s) { ...

  7. ES6学习历程(变量的声明)

    2019-01-25: 一:变量的声明: 1.对于变量的声明添加了let,const两种方式 关于let: (1)不存在变量提升--必须先声明再使用; (2)会出现暂时性死区--在一个方法外用var声 ...

  8. Beauty of Array ZOJ - 3872(思维题)

    Edward has an array A with N integers. He defines the beauty of an array as the summation of all dis ...

  9. Java 内存模型与线程

    when ? why ? how ? what ? 计算机的运行速度和它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘I/O .网络通信或者数据库访问上.如何把处理器的运算能力"压 ...

  10. models中字段参数blank和null的用法区别

    blank当blank=True时,说明此处的数据可以不填,默认情况下为False,也就意味着默认情况下,所输入的数据不得空,blank是和页面表单有关,在页面需要输入参数的时候,如果在models里 ...