题目描述

考古学家发现了一堵写有未知语言的白色墙壁,上面有一个n行m列的格子,其中有些格子内被填入了某个A至Z的大写字母,还有些格子是空白的。

一直横着或竖着的连续若干个字母会形成一个单词,且每一行的阅读顺序可能是从左向右或从右向左,每一列的阅读顺序可能是从下往上或从上往下。也就是说对于每一行来说,从左向右可以被看做是若干个单词形成的句子,相邻两个单词被一个或多个空白格子分割开来;也有可能是从右向左被看成是一个句子,竖直方向类似。

遗憾的是,我们并不完全知道每一行每一列的阅读顺序是怎样的。但可以猜测,有些单词会满足反转过来也是一个单词。例如单词BOY,翻转过来的YOB也是一个英文单词。

此外观察者发现,对每一行(列)来说,按照确定后的阅读顺序读出的所有单词同时满足“自己的字典序不小于翻转后的字典序”,或同时满足“自己的字典序不大于翻转后的字典序”。

在确定了所有行列的阅读顺序之后,我们可以构造出关于这种未知语言的字典。

请问字典中出现的“翻转过来也是一个单词”的单词最少有多少种请注意,如果一个单词翻转后是不同的另外一个单词,它们需要被分别计入;而对于本身是回文的单词则不需要重复计入

输入输出格式

输入格式:

第一行一个整数T,表示T组测试数据。

对于每一组数据来说:第一行输入两个整数n,m。

第二行给出了n个数字,对应n行,其中若第i个数字为1,则表示第i行的阅读顺序从左往右;若为-1则为从右向左;若为0则表示无法确定

第三行给出了m个数字,对应n行,其中若第i个数字为1,则表示第i列的阅读顺序从上往下;若为-1则为从下向上;若为0则表示无法确定

之后n行,每行给出了长度为m的字符串,由A~Z和下划线组成,对应了每个格子的符号,其中下划线表示格子为空。

输出格式:

输出T行。每一组数据输出一行一个整数,表示最少有多少个单词,满足翻转后依然是单词。

注意,如果一个单词是回文,那么它一定满足“翻转后依旧是单词”

输入输出样例

输入样例#1:

1

2 10

0 0

0 0 0 0 0 0 0 0 0 0

ADA_JARVIS

ADA_SIVRAJ

输出样例#1:

>3

说明

对于100%的数据,1<=n,m<=72,T<=64


题解

最小割

好久不写网络流现在我的网络流水平真低==

然后想了想似乎\(dp\)并不容易记录状态,那就应该是一个网络流了

发现要求正着倒着都存在的单词的对数最小

那么要不是个费用流,要不就是最小割,要不就是反面计数了

然后分析搜题解一下觉得应该是最小割

最小割连的一条边就表示把ta们分在不同集合的代价

那么可以发现回文串无论怎么翻转贡献都是1

所以不考虑回文串

然后考虑其他的单词

对于一个单词,如果这个单词和翻转后的这个单词同时出现那么就会产生2的贡献

所以我们对于每对单词,字典序小的往字典序大的单词连一条边权为2的边

然后再考虑读法的问题

可以发现行列没啥关系,所以相同方法考虑即可

如果给定读法是从左往右且这样读的单词字典序比倒着读小

那么就从\(S\)往该行连一条\(INF\)的边,然后这行向从左往右读的这行的单词连一条\(INF\)的边

如果给定读法是从左往右且这样读的单词字典序比倒着读大

那么就从这一行从左往右读的单词往这一行连一条\(INF\)的边,这行往\(T\)连一条\(INF\)的边

这样在跑最大流的时候如果一种单词的字典序较小的部分有流量流入就说明读出了这个单词

如果一种单词的字典序较大的部分有流量流入同样也说明读出了这个单词

那么这样就要割掉这条连着两个单词的边产生\(2\)的代价

从右往左读也是同理的

那么问题就是如果ta不给定你读法应该怎么办?

也就是说ta可以正着读也可以反着读

那么就同时连ta正着读的边和反着读的边

但是不连\(S,T\)到ta的边

这样跑一边最大流然后加上回文串就是答案了

代码

  1. #include<map>
  2. #include<queue>
  3. #include<cstdio>
  4. #include<cstring>
  5. #include<iostream>
  6. #include<algorithm>
  7. # define ull unsigned long long
  8. const int N = 80 ;
  9. const int M = 3005 ;
  10. const ull Base = 233 ;
  11. const int INF = 1e8 ;
  12. using namespace std ;
  13. inline int read() {
  14. char c = getchar() ; int x = 0 , w = 1 ;
  15. while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
  16. while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
  17. return x*w ;
  18. }
  19. char s[N][N] ;
  20. int n , m , S , T ;
  21. int ans , cnt , num = 1 ;
  22. int dirh[N] , dirl[N] , hea[M] ;
  23. int hnum[N] , lnum[N] , d[M] ;
  24. int hw[2][N][N] , lw[2][N][N] , isbig[M] ;
  25. ull pw[N] , hsh1[N] , hsh2[N] ;
  26. map < ull , int > p , hap ;
  27. struct E {
  28. int nxt , to , dis ;
  29. } edge[M * 20] ;
  30. inline void Insert_edge(int from , int to , int dis) {
  31. edge[++num].nxt = hea[from] ; edge[num].to = to ;
  32. edge[num].dis = dis ; hea[from] = num ;
  33. }
  34. inline void add_edge(int u , int v , int w) {
  35. Insert_edge(u , v , w) ;
  36. Insert_edge(v , u , 0) ;
  37. }
  38. inline void Clear() {
  39. ans = 0 ; cnt = 0 ; num = 1 ;
  40. memset(isbig , false , sizeof(isbig)) ;
  41. memset(hea , 0 , sizeof(hea)) ;
  42. memset(hnum , 0 , sizeof(hnum)) ;
  43. memset(lnum , 0 , sizeof(lnum)) ;
  44. p.clear() ; hap.clear() ;
  45. }
  46. inline bool Bfs() {
  47. queue < int > q ; q.push(S) ;
  48. memset(d , 0 , sizeof(d)) ; d[S] = 1 ;
  49. while(!q.empty()) {
  50. int u = q.front() ; q.pop() ;
  51. for(int i = hea[u] ; i ; i = edge[i].nxt) {
  52. int v = edge[i].to ;
  53. if(!d[v] && edge[i].dis > 0) {
  54. d[v] = d[u] + 1 ;
  55. q.push(v) ;
  56. }
  57. }
  58. }
  59. return d[T] ;
  60. }
  61. int Dfs(int u , int dis) {
  62. if(u == T || !dis) return dis ;
  63. int Sum = 0 ;
  64. for(int i = hea[u] ; i ; i = edge[i].nxt) {
  65. int v = edge[i].to ;
  66. if(d[v] == d[u] + 1 && edge[i].dis > 0) {
  67. int diss = Dfs(v , min(dis , edge[i].dis)) ;
  68. if(diss > 0) {
  69. edge[i].dis -= diss ; edge[i ^ 1].dis += diss ;
  70. dis -= diss ; Sum += diss ; if(!dis) break ;
  71. }
  72. }
  73. }
  74. if(!Sum) d[u] = -1 ;
  75. return Sum ;
  76. }
  77. inline void dinic() {
  78. while(Bfs())
  79. ans += Dfs(S , INF) ;
  80. }
  81. int main() {
  82. int Case = read() ;
  83. pw[0] = 1 ; for(int i = 1 ; i <= 75 ; i ++) pw[i] = pw[i - 1] * Base ;
  84. while(Case --) {
  85. Clear() ;
  86. n = read() ; m = read() ;
  87. for(int i = 1 ; i <= n ; i ++) dirh[i] = read() ;
  88. for(int i = 1 ; i <= m ; i ++) dirl[i] = read() ;
  89. for(int i = 1 ; i <= n ; i ++) {
  90. scanf("%s",s[i] + 1) ;
  91. for(int j = 1 ; j <= m ; j ++)
  92. hsh1[j] = hsh1[j - 1] * Base + s[i][j] ;
  93. for(int j = m ; j >= 1 ; j --)
  94. hsh2[j] = hsh2[j + 1] * Base + s[i][j] ;
  95. int lst = 1 ; ull tp1 , tp2 ;
  96. for(int j = 1 , l , r ; j <= m ; j ++) {
  97. if(s[i][j] == '_' || j == m) {
  98. l = lst , r = j - 1 ; lst = j + 1 ;
  99. if(s[i][j] != '_') ++ r ; if(l > r) continue ;
  100. tp1 = hsh1[r] - hsh1[l - 1] * pw[r - l + 1] ;
  101. tp2 = hsh2[l] - hsh2[r + 1] * pw[r - l + 1] ;
  102. if(tp1 == tp2) {
  103. if(hap[tp1]) continue ;
  104. ++ ans ; hap[tp1] = 1 ; continue ;
  105. }
  106. if(!p[tp1]) {
  107. int vit1 = 1 ;
  108. for(int k = 1 ; k <= r - l + 1 ; k ++) {
  109. if(s[i][l + k - 1] > s[i][r - k + 1]) break ;
  110. else if(s[i][l + k - 1] < s[i][r - k + 1]) {
  111. vit1 = -1 ;
  112. break ;
  113. }
  114. }
  115. p[tp1] = ++ cnt ; isbig[cnt] = vit1 ;
  116. p[tp2] = ++ cnt ; isbig[cnt] = - vit1 ;
  117. }
  118. hw[0][i][++hnum[i]] = p[tp1] ; hw[1][i][hnum[i]] = p[tp2] ;
  119. }
  120. }
  121. }
  122. for(int j = 1 ; j <= m ; j ++) {
  123. for(int i = 1 ; i <= n ; i ++)
  124. hsh1[i] = hsh1[i - 1] * Base + s[i][j] ;
  125. for(int i = n ; i >= 1 ; i --)
  126. hsh2[i] = hsh2[i + 1] * Base + s[i][j] ;
  127. int lst = 1 ; ull tp1 , tp2 ;
  128. for(int i = 1 , l , r ; i <= n ; i ++) {
  129. if(s[i][j] == '_' || i == n) {
  130. l = lst , r = i - 1 ; lst = i + 1 ;
  131. if(s[i][j] != '_') ++ r ; if(l > r) continue ;
  132. tp1 = hsh1[r] - hsh1[l - 1] * pw[r - l + 1] ;
  133. tp2 = hsh2[l] - hsh2[r + 1] * pw[r - l + 1] ;
  134. if(tp1 == tp2) {
  135. if(hap[tp1]) continue ;
  136. ++ ans ; hap[tp1] = 1 ; continue ;
  137. }
  138. if(!p[tp1]) {
  139. int vit1 = 1 ;
  140. for(int k = 1 ; k <= r - l + 1 ; k ++) {
  141. if(s[l + k - 1][j] > s[r - k + 1][j]) break ;
  142. else if(s[l + k - 1][j] < s[r - k + 1][j]) {
  143. vit1 = -1 ;
  144. break ;
  145. }
  146. }
  147. p[tp1] = ++ cnt ; isbig[cnt] = vit1 ;
  148. p[tp2] = ++ cnt ; isbig[cnt] = -vit1 ;
  149. }
  150. lw[0][j][++lnum[j]] = p[tp1] ; lw[1][j][lnum[j]] = p[tp2] ;
  151. }
  152. }
  153. }
  154. S = 0 , T = cnt + n + m + 1 ;
  155. for(int i = 1 , x , y ; i <= cnt ; i += 2) {
  156. x = i ; y = i + 1 ;
  157. if(isbig[x] == 1) swap(x , y) ;
  158. add_edge(x , y , 2) ;
  159. }
  160. for(int i = 1 ; i <= n ; i ++) {
  161. if(!hnum[i]) continue ;
  162. if((dirh[i] == 1 && isbig[hw[0][i][1]] < 0) || (dirh[i] == -1 && isbig[hw[1][i][1]] < 0)) {
  163. add_edge(S , cnt + i , INF) ;
  164. for(int j = 1 ; j <= hnum[i] ; j ++) {
  165. if(dirh[i] == 1) add_edge(cnt + i , hw[0][i][j] , INF) ;
  166. else add_edge(cnt + i , hw[1][i][j] , INF) ;
  167. }
  168. }
  169. else if((dirh[i] == 1 && isbig[hw[0][i][1]] > 0) || (dirh[i] == -1 && isbig[hw[1][i][1]] > 0)) {
  170. add_edge(cnt + i , T , INF) ;
  171. for(int j = 1 ; j <= hnum[i] ; j ++) {
  172. if(dirh[i] == 1) add_edge(hw[0][i][j] , cnt + i , INF) ;
  173. else add_edge(hw[1][i][j] , cnt + i , INF) ;
  174. }
  175. }
  176. else {
  177. for(int j = 1 , x , y ; j <= hnum[i] ; j ++) {
  178. x = hw[0][i][j] , y = hw[1][i][j] ;
  179. if(isbig[x] > 0) swap(x , y) ;
  180. add_edge(cnt + i , x , INF) ;
  181. add_edge(y , cnt + i , INF) ;
  182. }
  183. }
  184. }
  185. for(int i = 1 ; i <= m ; i ++) {
  186. if(!lnum[i]) continue ;
  187. if((dirl[i] == 1 && isbig[lw[0][i][1]] <= 0) || (dirl[i] == -1 && isbig[lw[1][i][1]] <= 0)) {
  188. add_edge(S , cnt + n + i , INF) ;
  189. for(int j = 1 ; j <= lnum[i] ; j ++) {
  190. if(dirl[i] == 1) add_edge(cnt + n + i , lw[0][i][j] , INF) ;
  191. else add_edge(cnt + n + i , lw[1][i][j] , INF) ;
  192. }
  193. }
  194. else if((dirl[i] == 1 && isbig[lw[0][i][1]] >= 0) || (dirl[i] == -1 && isbig[lw[1][i][1]] >= 0)) {
  195. add_edge(cnt + n + i , T , INF) ;
  196. for(int j = 1 ; j <= lnum[i] ; j ++) {
  197. if(dirl[i] == 1) add_edge(lw[0][i][j] , cnt + n + i , INF) ;
  198. else add_edge(lw[1][i][j] , cnt + n + i , INF) ;
  199. }
  200. }
  201. else {
  202. for(int j = 1 , x , y ; j <= lnum[i] ; j ++) {
  203. x = lw[0][i][j] , y = lw[1][i][j] ;
  204. if(isbig[x] > 0) swap(x , y) ;
  205. add_edge(cnt + n + i , x , INF) ;
  206. add_edge(y , cnt + n + i , INF) ;
  207. }
  208. }
  209. }
  210. dinic() ;
  211. printf("%d\n",ans) ;
  212. }
  213. return 0 ;
  214. }

[SDOI2016]墙上的句子的更多相关文章

  1. luogu P4076 [SDOI2016]墙上的句子

    luogu loj 题意看了我半天(逃 (应该是我语文太差了) 题意是要确定每一行和每一列的看单词的顺序,使得同时正着出现和反着出现在里面的单词数量最少,每行和每列的性质是这一行所有单词反过来的单词要 ...

  2. 【LOJ】#2066. 「SDOI2016」墙上的句子

    题解 我一直也不会网络流--orz 我们分析下这道题,显然和行列没啥关系,就是想给你n + m个串 那么我们对于非回文单词之外的单词,找到两两匹配的反转单词(即使另一个反转单词不会出现也要建出来) 具 ...

  3. Solution -「SDOI 2016」「洛谷 P4076」墙上的句子

    \(\mathcal{Description}\)   Link.   (概括得说不清话了还是去看原题吧 qwq. \(\mathcal{Solution}\)   首先剔除回文串--它们一定对答案产 ...

  4. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  5. [LeetCode] Sentence Screen Fitting 调整屏幕上的句子

    Given a rows x cols screen and a sentence represented by a list of words, find how many times the gi ...

  6. BZOJ4516: [Sdoi2016]生成魔咒 后缀自动机

    #include<iostream> #include<cstdio> #include<cstring> #include<queue> #inclu ...

  7. bzoj-4518 4518: [Sdoi2016]征途(斜率优化dp)

    题目链接: 4518: [Sdoi2016]征途 Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地 ...

  8. vim 长句子中的上下移动

    当一个句子很长的时候,屏幕显示不下,就会分为多行,这个时候,你又想找到中间几行某部分的字母,怎么办?这个时候,先按下一个 g ,在按下 j / k ,就可以实现长句子的上下移动了.

  9. BZOJ 4517: [Sdoi2016]排列计数

    4517: [Sdoi2016]排列计数 Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 911  Solved: 566[Submit][Status ...

随机推荐

  1. Java调用WSDL接口

    1.首先准备jar包: 2.代码调用如下: String url="url地址"; QName qName=new QName("命名空间","接口名 ...

  2. 【c++】C++中const用法总结

    1.      const常量,如const int max = 100; 优点:const常量有数据类型,而宏常量没有数据类型.编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全 ...

  3. 开源yYmVc项目,邀您和我一起开发:)

    打算在闲暇时间写个MVC框架,要有什么功能一步一步边写边加,仿照struts 2 和 spring mvc.假设您感兴趣的话,能够私密我,给您加入key:). 欢迎您的到来~ 项目放在基于GIT的CS ...

  4. js将月份转换为英文简写的形式

    本文通过代码实例介绍一下如何实现将月份转换为英文简写的形式. 比如将一月份转换为Jan,需要的朋友可以做一下参考. 代码实例如下: [JavaScript] 纯文本查看 复制代码运行代码 1 2 3 ...

  5. 李洪强iOS开发之录音和播放实现

    李洪强iOS开发之录音和播放实现 //首先导入框架后,导入头文件.以下内容为托控件,在storyboard中拖出两个按钮为录音和播放按钮 //创建一个UIViewController在.h文件中写 # ...

  6. 【iOS系列】-程序开启后台运行

    [iOS系列]-程序开启后台运行 iOS程序是伪后台的运行,可是有时候我们需要让其在后台也要进行一些操作,我们可以让其伪装成音乐的APP,这样就可以让程序后台进行相关操作了,具体做法如下: 1:在Ap ...

  7. Webx框架:依赖注入

    Webx的依赖注入和Spring的依赖注入很像,仅仅是有一点点的差别. 注入的时候仅仅能让生命周期长的注入到生命周期短的对象中,比方requestScope对象注入到singleton时就会错误发生. ...

  8. YTU 2580: 改错题----修改revert函数

    2580: 改错题----修改revert函数 时间限制: 1 Sec  内存限制: 128 MB 提交: 194  解决: 82 题目描述 修改revert函数,实现输入N个数,顺序倒置后输出 #i ...

  9. JavaScript 图片广告自动与手动的切换

    ​1.代码 <html> <head>   <script type="text/javascript" src="jquery-1.8.j ...

  10. 如何用Mac远程桌面连接windows

    打开mac,连接网络,找到系统中自带的“远程桌面连接”软件,截图如下