【题解】C2Crni - Crni [COCI2010] [SP7884]

传送门:\(\text{C2Crni - Crni}\) \(\text{[COCI2010]}\) \(\text{[SP7884]}\)

【题目描述】

给定一个 \(\text{N} * \text{N}\) 的矩阵,每个格子要么为白色(\(B\))要么为黑色(\(C\))。定义黑矩形为所含单元格数大于等于 \(2\) 且所含单元格均为黑色的矩阵。

如图:

左边的两个矩形都不是黑矩形,因为 \(1\) 中有白格,\(2\) 的大小为 \(1\),而右图的 \(3\) 个都是黑矩形。

要解决的问题是在给定的矩形中找出两个没有共公部分的黑矩形,输出所有方案数,由于数较大,答案对 \(10007\) 取模。

【样例】

  1. 样例输入:
  2. 2
  3. CC
  4. CC
  5. 样例输出:
  6. 2
  7. 样例输入:
  8. 3
  9. CCB
  10. CCB
  11. CBB
  12. 样例输出:
  13. 5
  14. 样例输入:
  15. 5
  16. BCCBB
  17. BBCBB
  18. BCCBB
  19. BBBBB
  20. CCBBB
  21. 样例输出:
  22. 8

【数据范围】

\(100 \%:\) \(1 \leqslant n \leqslant 1000\)


【分析】

这是一道套路题,用到了很多关于矩阵的处理技巧,但找到解决方法后会发现它的思维难度其实并不高,主要是代码实现较困难,所以也可以视其为膜你题。

【前缀和的套路】

找子矩阵基本都会用到前缀和,常见的查询子矩阵可以直接容斥,例如维护二维树状数组时用到的方法:

设 \(S[x][y]=\sum_{i=1}^{x} \sum_{j=1}^{y} a[i][j]\),那么递推式为 \(S[i][j]=\) \(S[i-1][j]+S[i][j-1]-S[i-1][j-1]+a[i][j]\)

如果要查询以 \((x1,y1)\) 为左下角,以 \((x2,y2)\) 为右下角的矩阵和,\(\sum_{i={x_1}}^{y_1} \sum_{j={x_2}}^{y_2} a[i][j]=\) \(S[x2][y2]-S[x1-1][y2]-S[x2][y1-1]+S[x1-1][y1-1]\)

在预处理式子时需要从左上角一直递推到右下角,而稍复杂一点的需要统计多个方向(没错,就是此题了),即从最多 \(4\) 个角落(左上,左下,右上,右下)开始向其对角处递推,得到多个助于统计答案的前缀和数组。

【预处理】

回到此题。

为方便处理,将矩阵中的黑点设为 \(1\),白点设为 \(0\) 。

对于所有的黑点,先预处理出 \(4\) 个数组:

\((1).\) \(RD[i][j]\): 以 \((i,j)\) 为右下角黑矩阵个数。

\((2).\) \(LU[i][j]\): 以 \((i,j)\) 为左上角黑矩阵个数。

\((3).\) \(LD[i][j]\): 以 \((i,j)\) 为左下角黑矩阵个数。

\((4).\) \(RU[i][j]\): 以 \((i,j)\) 为右上角黑矩阵个数。

但如果暴力枚举的话 \(O(n^4)\) 复杂度过高,需要考虑合理继承前面求出的信息。

以 \(RD\) 为例,为便于推导,我们先在矩阵中枚举一条辅助线,假设已经求出了第 \(i\) 行前 \(j-1\) 列的 \(RD\) 信息,如图为 \(i=4,j=4\) 的情况:

定义 \(H[i][j]\) 为点 \((i,j)\) 向上最多可以延伸的距离(或者说高度),如果 \(a(i,j)\) 为白块,\(H[i][j]=0\) 。

处理方法如下:

对于点 \((i,j)\) 找到同一列前面第一个 \(H\) 小于它的位置 \((i,k)\)。

由于 \([k+1,j]\) 的高度都大于 \(j\),那么将会有 \(H[i][j]*(j-k)\) 个点可以作为黑矩形左上角右下角为 \((i,j)\)),但是将 \((i,j)\) 自己作为左上角黑矩阵大小只有 \(1\),所以要减去 \(1\) 。

另外以 \((i,k)\) 为右下角黑矩阵都可以将长度扩大 \(j-k\),即变成以 \((i,j)\) 为右下角,但以 \((i,k)\) 为右下角的情况没有计算在 \(RD[i][k]\) 以内,所以要加上 \(1\) 。

得到递推式为:\(RD[i][j]=H[i][j]*(j-k)-1+RD[i][k]+1\) 。

于是时间复杂度就被优化到了 \(O(n^3)\),但还不够优秀。

现在的问题是如何快速找 \(k\),方法同 \(\text{Largest}\) \(\text{Rectangle}\) \(\text{in}\) \(\text{a}\) \(\text{Histogram}\) (题解),直接单调栈维护即可。

在上面那张图中 \(H[4][1]=1,H[4][2]=2,H[4][3]=4,H[4][4]=3\),所以 \(j=4\) 时的决策点 \(k=2\),因此 \(RD[3][4]=3*2-1+RD[3][2]+1\) 。

同理可得 \(LU,LD,RU\) 。

【统计答案】

依旧是枚举辅助线:

先求出下边界在红线上面黑矩形个数,即 \(\sum_{i=1}^{x} \sum_{j=1}^{n} RD[i][j]\)(或者 \(LD[i][j]\)),

再求出上边界紧贴在红线下面黑矩阵个数,即 \(\sum_{j=1}^{n}LU[x+1][j]\)(或者 \(RU[x+1][j]\)),

将二者相乘,再对于每一条辅助线算出的结果求和,得到相对位置为上下黑矩形总对数。(其实也可以固定红线上面,红线下面求总个数)

同理枚举竖线,可得相对位置为左右黑矩形总对数。

但这样会有算重复的情况,如下图绿色部分和蓝色部分:

因此还要减去相对位置既有上下又有左右黑矩形对数,也就是在十字线对角象限的黑矩形对数,求法和前面大致相同。为方便处理,要任选两个方向计算矩阵前缀和(递推式和二维树状数组的一样):

\((1).\) \(S_{RD}[x][y]=\sum_{i=1}^{x} \sum_{j=1}^{y} RD[i][j]\)

前缀和递推方向:左上 \(\text{→}\) 右下

矩阵前缀和意义:右下角在 \((i,j)\) 左上面的黑矩阵个数。

\((2).\) \(S_{LU}[x][y]=\sum_{i=n}^{x} \sum_{j=n}^{y} RD[i][j]\)

前缀和递推方向:右下 \(\text{→}\) 左上

矩阵前缀和意义:左上角在 \((i,j)\) 右下面的黑矩阵个数。

\((3).\) \(S_{LD}[x][y]=\sum_{i=1}^{x} \sum_{j=n}^{y} RD[i][j]\)

前缀和递推方向:右上 \(\text{→}\) 左下

矩阵前缀和意义:左下角在 \((i,j)\) 右上面的黑矩阵个数。

\((4).\) \(S_{RU}[x][y]=\sum_{i=n}^{x} \sum_{j=1}^{y} RD[i][j]\)

前缀和递推方向:左下 \(\text{→}\) 右上

矩阵前缀和意义:右上角在 \((i,j)\) 左下面的黑矩阵个数。

最后,此题细节较多,变量名没设好的话很容易搞混。

时间复杂度为:\(O(n^2)\) 。


【Code】

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<cstdio>
  6. #define Re register int
  7. #define For(i,a,b) for(Re i=a;i<=b;++i)
  8. #define Por(i,a,b) for(Re i=a;i>=b;--i)
  9. #define print() for(Re i=1;i<=n;puts(""),++i)for(Re j=1;j<=n;++j)
  10. using namespace std;
  11. const int N=1003,P=10007;
  12. int n,Q[N],A[N][N],H[N][N],SS[N][N];char ch[N];
  13. inline void in(Re &x){
  14. int f=0;x=0;char c=getchar();
  15. while(c<'0'||c>'9')f|=c=='-',c=getchar();
  16. while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
  17. x=f?-x:x;
  18. }
  19. int RD[N][N];
  20. inline void get_RD(){//RD[i][j]: 以i,j为右下角的黑矩形个数(1,1)→(n,n)
  21. memset(H,0,sizeof(H));
  22. For(i,1,n)For(j,1,n)if(A[i][j])H[i][j]=H[i-1][j]+1;
  23. // print()printf("%d ",H[i][j]);puts("");
  24. For(i,1,n){
  25. Re h=1,t=0;
  26. For(j,1,n)if(!A[i][j])RD[i][j]=-1;
  27. RD[i][Q[++t]=0]=-1;
  28. For(j,1,n){
  29. while(h<=t&&H[i][Q[t]]>=H[i][j])--t;
  30. if(h<=t&&A[i][j])RD[i][j]=RD[i][Q[t]]+1+H[i][j]*(j-Q[t])-1;
  31. Q[++t]=j;
  32. }
  33. For(j,1,n)if(RD[i][j]<0)RD[i][j]=0;
  34. }
  35. // print()printf("%d ",RD[i][j]);puts("");
  36. }
  37. int LU[N][N];
  38. inline void get_LU(){//LU[i][j]: 以i,j为左上角的黑矩形个数(n,n)→(1,1)
  39. memset(H,0,sizeof(H));
  40. Por(i,n,1)Por(j,n,1)if(A[i][j])H[i][j]=H[i+1][j]+1;
  41. // print()printf("%d ",H[i][j]);puts("");
  42. Por(i,n,1){
  43. Re h=1,t=0;
  44. Por(j,n,1)if(!A[i][j])LU[i][j]=-1;
  45. LU[i][Q[++t]=n+1]=-1;
  46. Por(j,n,1){
  47. while(h<=t&&H[i][Q[t]]>=H[i][j])--t;
  48. if(h<=t&&A[i][j])LU[i][j]=LU[i][Q[t]]+1+H[i][j]*(Q[t]-j)-1;
  49. Q[++t]=j;
  50. }
  51. Por(j,n,1)if(LU[i][j]<0)LU[i][j]=0;
  52. }
  53. // print()printf("%d ",LU[i][j]);puts("");
  54. }
  55. int LD[N][N];
  56. inline void get_LD(){//LD[i][j]: 以i,j为左下角的黑矩形个数(1,n)→(n,1)
  57. memset(H,0,sizeof(H));
  58. For(i,1,n)Por(j,n,1)if(A[i][j])H[i][j]=H[i-1][j]+1;
  59. // print()printf("%d ",H[i][j]);puts("");
  60. For(i,1,n){
  61. Re h=1,t=0;
  62. Por(j,n,1)if(!A[i][j])LD[i][j]=-1;
  63. LD[i][Q[++t]=n+1]=-1;
  64. Por(j,n,1){
  65. while(h<=t&&H[i][Q[t]]>=H[i][j])--t;
  66. if(h<=t&&A[i][j])LD[i][j]=LD[i][Q[t]]+1+H[i][j]*(Q[t]-j)-1;
  67. Q[++t]=j;
  68. }
  69. Por(j,n,1)if(LD[i][j]<0)LD[i][j]=0;
  70. }
  71. // print()printf("%d ",LD[i][j]);puts("");
  72. }
  73. int RU[N][N];
  74. inline void get_RU(){//RU[i][j]: 以i,j为右上角的黑矩形个数(n,1)→(1,n)
  75. memset(H,0,sizeof(H));
  76. Por(i,n,1)Por(j,n,1)if(A[i][j])H[i][j]=H[i+1][j]+1;
  77. // print()printf("%d ",H[i][j]);puts("");
  78. Por(i,n,1){
  79. Re h=1,t=0;
  80. For(j,1,n)if(!A[i][j])RU[i][j]=-1;
  81. RU[i][Q[++t]=0]=-1;
  82. For(j,1,n){
  83. while(h<=t&&H[i][Q[t]]>=H[i][j])--t;
  84. if(h<=t&&A[i][j])RU[i][j]=RU[i][Q[t]]+1+H[i][j]*(j-Q[t])-1;
  85. Q[++t]=j;
  86. }
  87. For(j,1,n)if(RU[i][j]<0)RU[i][j]=0;
  88. }
  89. // print()printf("%d ",RU[i][j]);puts("");
  90. }
  91. inline int U_D(){//加上-下
  92. Re ans=0,S=0;
  93. For(i,1,n){
  94. For(j,1,n)(ans+=S*LU[i][j]%P)%=P;//用【左上角为(i,j)的矩阵LU】固定在辅助线下面
  95. For(j,1,n)(S+=RD[i][j])%=P;//用【右下角为(i,j)的矩阵RD】求辅助线上边的总个数
  96. }
  97. return ans%P;
  98. }
  99. inline int L_R(){//加左-右
  100. Re ans=0,S=0;
  101. For(j,1,n){
  102. For(i,1,n)(ans+=S*LU[i][j]%P)%=P;//用【左上角为(i,j)的矩阵LU】固定在辅助线右边
  103. For(i,1,n)(S+=RD[i][j])%=P;//用【右下角为(i,j)的矩阵RD】求辅助线左边的总个数
  104. }
  105. return ans%P;
  106. }
  107. inline int LU_RD(){//减左上-右下
  108. Re ans=0;memset(SS,0,sizeof(SS));
  109. For(i,1,n-1)For(j,1,n-1){
  110. SS[i][j]=((RD[i][j]+SS[i-1][j]+SS[i][j-1])%P-SS[i-1][j-1]+P)%P;
  111. //十字线左上角的用【右下角为(i,j)的矩阵RD】求总和
  112. (ans+=SS[i][j]*LU[i+1][j+1]%P)%=P;//用【左上角为(i,j)的矩阵LU】固定十字线的右下角
  113. }
  114. return ans;
  115. }
  116. inline int RU_LD(){//减右上-左下
  117. Re ans=0;memset(SS,0,sizeof(SS));
  118. For(i,1,n-1)Por(j,n,2){
  119. SS[i][j]=((LD[i][j]+SS[i-1][j]+SS[i][j+1])%P-SS[i-1][j+1]+P)%P;
  120. //十字线右上角的用【左下角为(i,j)的矩阵LD】求总和
  121. (ans+=SS[i][j]*RU[i+1][j-1]%P)%=P;//用【右上角为(i,j)的矩阵RU】固定十字线的左下角
  122. }
  123. return ans;
  124. }
  125. int main(){
  126. // freopen("crni.in","r",stdin);
  127. // freopen("crni.out","w",stdout);
  128. in(n);
  129. For(i,1,n){
  130. scanf("%s",ch+1);
  131. For(j,1,n)A[i][j]=(ch[j]=='C');
  132. }
  133. get_RD(),get_LU(),get_LD(),get_RU();
  134. printf("%d\n",((U_D()+L_R())%P-(LU_RD()+RU_LD())%P+P)%P);
  135. // fclose(stdin);
  136. // fclose(stdout);
  137. return 0;
  138. }

【题解】C2Crni - Crni [COCI2010] [SP7884]的更多相关文章

  1. 【简解】C2CRNI - Crni

    [题目大意] 给定一个N行N列的矩阵,每个格子要么为白色要么为黑色.黑矩形为所涵单元格数大于等于2且所涵单元格均为黑色的矩表.要解决的问题是在给定的矩形中找出两个没有共公部分的黑矩形,输出所有方案数, ...

  2. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  3. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  4. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  5. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  6. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  7. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  8. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  9. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

随机推荐

  1. Vue计算属性computed的全面解析

    前言 一直以来对computed这个计算属性都只停在一个大概的认知中,最近特意仔细研读相关资料,亲测后逐渐了解了其特性. 正文 computed 特点: 1.初始化/依赖属性(即data属性)改变时执 ...

  2. CTF挑战赛丨网络内生安全试验场第一季答题赛火热开启

    前期回顾:挑战世界级“人机大战”,更有万元奖金等你来拿 网络内生安全试验场自上线以来,受到了业内的极大重视与关注. 自9月2日报名通道开启后,报名量更是持续高升,上百名精英白帽踊跃报名. 至此,网络内 ...

  3. 英语NanyangJade南阳玉NanyangJade独山玉

    南阳玉NanyangJade又叫做独山玉,独山玉,因产于河南南阳的独山而得名,亦称“南阳玉”. 独山玉是中国四大名玉之一,有南阳翡翠之称,独山玉是一种黝帘石化斜长岩,由多种矿物组成,属多色玉器. 据文 ...

  4. Android 项目主要文件

    1.manifests下的AndroidManifest.xml是Andriod程序的清单文件,该文件是整个项目的配置文件,Android四大组件Activity.BroadcastReceiver. ...

  5. ajax的jQuery的表单序列化获取参数serialize()

    需要引入jQuery.js才能使用$("form表单的id").serialize()可获取form表单里面所有表单元素的值和name属性值,按顺序拼接成查询字符串格式为name值 ...

  6. docker的8个使用场景

    1.简化配置 虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台(软件, 系统), Docker在降低额外开销的情况下提供了同样的功能. 它能让你将运行环境和配置放在代码汇总然后部署, 同一 ...

  7. VC 静态库与动态库(一)介绍

    定义: 静态库与动态库都属于库,库从本质上来说就是种代码重用的方式. 把需要重复使用的公共代码抽离出来,生成库文件,外部程序只需包含库文件,调用相关接口即可 静态库与动态库区别: 静态库:需要库的.h ...

  8. MySQL数据库 外键,级联, 修改表的操作

    1.外键: 用来建立两张表之间的关系 - 一对多 - 多对多 - 一对一 研究表与表之间的关系: 1.定义一张 员工部门表 id, name, gender, dep_name, dep_desc - ...

  9. LeetCode 241. Different Ways to Add Parentheses为运算表达式设计优先级 (C++)

    题目: Given a string of numbers and operators, return all possible results from computing all the diff ...

  10. 团队冲刺--three

    今天学习css,用css做登录界面. 昨天学习了爬虫了初步. 问题:爬虫很难.