luogu

loj

题意看了我半天(逃 (应该是我语文太差了)

题意是要确定每一行和每一列的看单词的顺序,使得同时正着出现和反着出现在里面的单词数量最少,每行和每列的性质是这一行所有单词反过来的单词要么字典序大于等于原来的,要么小于等于原来的

首先回文单词一定会出现,可以直接加入答案,以后就不用管了.对于剩下的单词,如果不想让他贡献答案,那就要使的每次读这个单词都是一个样子,例如"ABC" "CBA",那么第一个正着读,第二个反着读答案最小.为了方便,我们只保留单词字典序更小的形式,同时把行列的读到单词字典序更小的方向作为正方向.然后现在问题变成给每一行每一列确定方向,使得所在行列同时有正方向和反方向的单词数量最小(就是给每个集合确定黑/白颜色,使得所在集合中有黑集合和白集合的元素个数最小)

这个可以使用最小割解决,具体是给每行每列建点,如果正方向可以用就从原点\(ps\)向这个点\(i\)连容量为\(1\)的边\((ps,i,1)\),否则连\((ps,i,+\infty)\),如果反方向可以用就从\(i\)向汇点\(pt\)连容量为\(1\)的边\((i,pt,1)\),否则连\((i,pt,+\infty)\),这样如果最后\(i\)在\(pt\)集合就是正方向,否则就是反方向.然后对于每个单词\(j\)建两个点\(a1_j,a2_j\),连边\((ps,a1_j,p),(a2_j,pt,p)(p\)为一个大于\(n+m\)的数\()\),然后对于单词\(j\)所在的行列\(i\),连边\((a1_j,i,+\infty),(i,a2_j,+\infty)\),这样如果一个单词所在行列同时割在\(ps\)或者\(pt\)集合中,就要多割去一个\(p\),不然要多割掉\(2p\)并且给答案加上\(2\)(一个非回文单词要统计两次).最后答案为回文单词个数+\(2(\lfloor\frac{flow}{p}\rfloor-\)非回文单词个数\()\)

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. #define uLL unsigned long long
  4. using namespace std;
  5. const int N=75,M=N*N*2,inf=1<<29;
  6. int rd()
  7. {
  8. int x=0,w=1;char ch=0;
  9. while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
  10. while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
  11. return x*w;
  12. }
  13. int to[M*4],nt[M*4],c[M*4],hd[M],tot=1;
  14. void add(int x,int y,int z)
  15. {
  16. ++tot,to[tot]=y,nt[tot]=hd[x],c[tot]=z,hd[x]=tot;
  17. ++tot,to[tot]=x,nt[tot]=hd[y],c[tot]=0,hd[y]=tot;
  18. }
  19. int ps,pt,nhd[M],lv[M];
  20. queue<int> q;
  21. bool bfs()
  22. {
  23. for(int i=ps;i<=pt;++i) lv[i]=0;
  24. lv[ps]=1,q.push(ps);
  25. while(!q.empty())
  26. {
  27. int x=q.front();
  28. q.pop();
  29. for(int i=hd[x];i;i=nt[i])
  30. {
  31. int y=to[i];
  32. if(c[i]>0&&!lv[y])
  33. {
  34. lv[y]=lv[x]+1;
  35. q.push(y);
  36. }
  37. }
  38. }
  39. return lv[pt];
  40. }
  41. int dfs(int x,int fw)
  42. {
  43. if(x==pt) return fw;
  44. int an=0;
  45. for(int &i=nhd[x];i;i=nt[i])
  46. {
  47. int y=to[i];
  48. if(c[i]>0&&lv[y]==lv[x]+1)
  49. {
  50. int dt=dfs(y,min(fw,c[i]));
  51. c[i]-=dt,c[i^1]+=dt;
  52. fw-=dt,an+=dt;
  53. if(!fw) break;
  54. }
  55. }
  56. return an;
  57. }
  58. int dinic()
  59. {
  60. int an=0,dt=0;
  61. while(bfs())
  62. {
  63. for(int i=ps;i<=pt;++i) nhd[i]=hd[i];
  64. while((dt=dfs(ps,inf))) an+=dt;
  65. }
  66. return an;
  67. }
  68. map<string,int> mp;
  69. vector<int> ls[N*N];
  70. string ss[N*N];
  71. char cc[N][N];
  72. int n,m,ans,w1[N],w2[N],tt;
  73. int main()
  74. {
  75. int T=rd();
  76. while(T--)
  77. {
  78. memset(hd,0,sizeof(hd)),tot=1;
  79. n=rd(),m=rd();
  80. ps=0,pt=n+m+n*m+1;
  81. for(int i=1;i<=n;++i) w1[i]=rd();
  82. for(int i=1;i<=m;++i) w2[i]=rd();
  83. for(int i=1;i<=n;++i) scanf("%s",cc[i]+1);
  84. mp.clear(),tt=0;
  85. for(int i=1;i<=n;++i)
  86. {
  87. string nw,rnw;
  88. bool fg=0;
  89. for(int j=1;j<=m+1;++j)
  90. {
  91. if(cc[i][j]&&cc[i][j]!='_') nw.push_back(cc[i][j]);
  92. else if(!nw.empty())
  93. {
  94. rnw=nw;
  95. reverse(rnw.begin(),rnw.end());
  96. if(!fg&&rnw!=nw)
  97. {
  98. fg=1;
  99. if(rnw<nw) nw=rnw,w1[i]=-w1[i];
  100. }
  101. if(!mp.count(nw)) mp[nw]=++tt,ss[tt]=nw,ls[tt].clear();
  102. ls[mp[nw]].push_back(i);
  103. nw.clear();
  104. }
  105. }
  106. }
  107. for(int j=1;j<=m;++j) cc[n+1][j]=0;
  108. for(int j=1;j<=m;++j)
  109. {
  110. string nw,rnw;
  111. bool fg=0;
  112. for(int i=1;i<=n+1;++i)
  113. {
  114. if(cc[i][j]&&cc[i][j]!='_') nw.push_back(cc[i][j]);
  115. else if(!nw.empty())
  116. {
  117. rnw=nw;
  118. reverse(rnw.begin(),rnw.end());
  119. if(!fg&&rnw!=nw)
  120. {
  121. fg=1;
  122. if(rnw<nw) nw=rnw,w2[j]=-w2[j];
  123. }
  124. if(!mp.count(nw)) mp[nw]=++tt,ss[tt]=nw,ls[tt].clear();
  125. ls[mp[nw]].push_back(j+n);
  126. nw.clear();
  127. }
  128. }
  129. }
  130. for(int i=1;i<=n;++i)
  131. {
  132. add(ps,i,w1[i]>=0?1:inf);
  133. add(i,pt,w1[i]<=0?1:inf);
  134. }
  135. for(int j=1;j<=m;++j)
  136. {
  137. add(ps,j+n,w2[j]>=0?1:inf);
  138. add(j+n,pt,w2[j]<=0?1:inf);
  139. }
  140. int pc=n+m,dt=0;
  141. ans=0;
  142. for(int i=1;i<=tt;++i)
  143. {
  144. string rnw=ss[i];
  145. reverse(rnw.begin(),rnw.end());
  146. if(ss[i]==rnw){++ans;continue;}
  147. dt+=233;
  148. ++pc,++pc;
  149. add(ps,pc-1,233),add(pc,pt,233);
  150. sort(ls[i].begin(),ls[i].end());
  151. vector<int>::iterator it;
  152. int la=0;
  153. for(it=ls[i].begin();it!=ls[i].end();++it)
  154. {
  155. int x=*it;
  156. if(x==la) continue;
  157. add(pc-1,x,inf),add(x,pc,inf);
  158. la=x;
  159. }
  160. }
  161. ans+=((dinic()-dt)/233)*2;
  162. printf("%d\n",ans);
  163. }
  164. return 0;
  165. }

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

  1. [SDOI2016]墙上的句子

    题目描述 考古学家发现了一堵写有未知语言的白色墙壁,上面有一个n行m列的格子,其中有些格子内被填入了某个A至Z的大写字母,还有些格子是空白的. 一直横着或竖着的连续若干个字母会形成一个单词,且每一行的 ...

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

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

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

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

  4. 【题解】Luogu P4069 [SDOI2016]游戏

    原题传送门 看到这种题,想都不用想,先写一个树链剖分 然后发现修改操作增加的是等差数列,这使我们想到了李超线段树 先进性树剖,然后用李超线段树维护区间最小,这样就做完了(写码很容易出错) 复杂度为\( ...

  5. Luogu P4070 [SDOI2016]生成魔咒

    题目链接 \(Click\) \(Here\) 其实是看后缀数组资料看到这个题目的,但是一眼反应显然后缀自动机,每次维护添加节点后的答案贡献即可,唯一不友好的一点是需要平衡树维护,这里因为复杂度不卡而 ...

  6. Luogu P4071 [SDOI2016]排列计数

    晚上XZTdalao给我推荐了这道数论题.太棒了又可以A一道省选题了 其实这道题也就考一个错排公式+组合数+乘法逆元 我们来一步一步分析 错排公式 通俗的说就是把n个1~n的数排成一个序列A,并使得所 ...

  7. Luogu 4069 [SDOI2016]游戏

    BZOJ 4515 树链剖分 + 李超线段树 要求支持区间插入一条线段,然后查询一个区间内的最小值.可以使用李超线段树解决,因为要维护一个区间内的最小值,所以每一个结点再维护一个$res$表示这个区间 ...

  8. Luogu P4068 [SDOI2016]数字配对

    反正现在做题那么少就争取做一题写一题博客吧 看到题目发现数字种类不多,而且结合价值的要求可以容易地想到使用费用流 但是我们如果朴素地建图就会遇到一个问题,若\(i,j\)符合要求,那么给\(i,j\) ...

  9. bzoj AC倒序

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

随机推荐

  1. JS框架_(AJAX)检测ip和地区

    百度云盘 传送门  密码:l94p 实现效果: <!DOCTYPE html> <html> <head> <meta charset="utf-8 ...

  2. 微信小程序_(视图)简单的scroll-view容器

    scroll-view容器效果 官方文档:传送门 scroll-view 可滚动视图区域 scroll-x Boolean false 允许横向滚动 scroll-y Boolean false 允许 ...

  3. 13.多线程设计模式 - Future模式

    多线程设计模式 - Future模式 并发设计模式属于设计优化的一部分,它对于一些常用的多线程结构的总结和抽象.与串行相比并行程序结构通常较为复杂,因此合理的使用并行模式在多线程并发中更具有意义. 1 ...

  4. JPA查询getOne()与findOne()的差异以及一些小问题

    起初用Jpa 里面 getOne() 查询一个id的数据 发现查询出来的数据都是空的,但不是空的对象是按照对象默认值来的 所以导致查询不出结果 以为是数据库修改,没有及时修改实体类导致的 但是后来发现 ...

  5. C++入门经典-例5.10-指针作为返回值

    1:代码如下: // 5.10.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> usin ...

  6. spark 笔记 16: BlockManager

    先看一下原理性的文章:http://jerryshao.me/architecture/2013/10/08/spark-storage-module-analysis/ ,http://jerrys ...

  7. mesh之孔洞检测

    mesh之孔洞检测 图1 检测孔洞点 图2 检测孔洞点 图3 检测孔洞点 图4 细节

  8. 整理ing

    RT 要学习的 专克bzoj权限题 钟神p系列

  9. xstream解析xml时遇到特殊字符出错

    在xml中有"&"符号时,解析xml出错 解决办法: 将&替换成&

  10. kotlin之字符串模板

    所谓字符串模板就是在字符串中添加若干个占位符,内容会在后期指定,也就是说,用模板可以设置字符串动态的部分,模板使用美元符号$设置如i=$i 中 的$i就是一个占位符,其中4后面的i是变量,随着i的变化 ...