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-\)非回文单词个数\()\)

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long using namespace std;
const int N=75,M=N*N*2,inf=1<<29;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
return x*w;
}
int to[M*4],nt[M*4],c[M*4],hd[M],tot=1;
void add(int x,int y,int z)
{
++tot,to[tot]=y,nt[tot]=hd[x],c[tot]=z,hd[x]=tot;
++tot,to[tot]=x,nt[tot]=hd[y],c[tot]=0,hd[y]=tot;
}
int ps,pt,nhd[M],lv[M];
queue<int> q;
bool bfs()
{
for(int i=ps;i<=pt;++i) lv[i]=0;
lv[ps]=1,q.push(ps);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(c[i]>0&&!lv[y])
{
lv[y]=lv[x]+1;
q.push(y);
}
}
}
return lv[pt];
}
int dfs(int x,int fw)
{
if(x==pt) return fw;
int an=0;
for(int &i=nhd[x];i;i=nt[i])
{
int y=to[i];
if(c[i]>0&&lv[y]==lv[x]+1)
{
int dt=dfs(y,min(fw,c[i]));
c[i]-=dt,c[i^1]+=dt;
fw-=dt,an+=dt;
if(!fw) break;
}
}
return an;
}
int dinic()
{
int an=0,dt=0;
while(bfs())
{
for(int i=ps;i<=pt;++i) nhd[i]=hd[i];
while((dt=dfs(ps,inf))) an+=dt;
}
return an;
}
map<string,int> mp;
vector<int> ls[N*N];
string ss[N*N];
char cc[N][N];
int n,m,ans,w1[N],w2[N],tt; int main()
{
int T=rd();
while(T--)
{
memset(hd,0,sizeof(hd)),tot=1;
n=rd(),m=rd();
ps=0,pt=n+m+n*m+1;
for(int i=1;i<=n;++i) w1[i]=rd();
for(int i=1;i<=m;++i) w2[i]=rd();
for(int i=1;i<=n;++i) scanf("%s",cc[i]+1);
mp.clear(),tt=0;
for(int i=1;i<=n;++i)
{
string nw,rnw;
bool fg=0;
for(int j=1;j<=m+1;++j)
{
if(cc[i][j]&&cc[i][j]!='_') nw.push_back(cc[i][j]);
else if(!nw.empty())
{
rnw=nw;
reverse(rnw.begin(),rnw.end());
if(!fg&&rnw!=nw)
{
fg=1;
if(rnw<nw) nw=rnw,w1[i]=-w1[i];
}
if(!mp.count(nw)) mp[nw]=++tt,ss[tt]=nw,ls[tt].clear();
ls[mp[nw]].push_back(i);
nw.clear();
}
}
}
for(int j=1;j<=m;++j) cc[n+1][j]=0;
for(int j=1;j<=m;++j)
{
string nw,rnw;
bool fg=0;
for(int i=1;i<=n+1;++i)
{
if(cc[i][j]&&cc[i][j]!='_') nw.push_back(cc[i][j]);
else if(!nw.empty())
{
rnw=nw;
reverse(rnw.begin(),rnw.end());
if(!fg&&rnw!=nw)
{
fg=1;
if(rnw<nw) nw=rnw,w2[j]=-w2[j];
}
if(!mp.count(nw)) mp[nw]=++tt,ss[tt]=nw,ls[tt].clear();
ls[mp[nw]].push_back(j+n);
nw.clear();
}
}
}
for(int i=1;i<=n;++i)
{
add(ps,i,w1[i]>=0?1:inf);
add(i,pt,w1[i]<=0?1:inf);
}
for(int j=1;j<=m;++j)
{
add(ps,j+n,w2[j]>=0?1:inf);
add(j+n,pt,w2[j]<=0?1:inf);
}
int pc=n+m,dt=0;
ans=0;
for(int i=1;i<=tt;++i)
{
string rnw=ss[i];
reverse(rnw.begin(),rnw.end());
if(ss[i]==rnw){++ans;continue;}
dt+=233;
++pc,++pc;
add(ps,pc-1,233),add(pc,pt,233);
sort(ls[i].begin(),ls[i].end());
vector<int>::iterator it;
int la=0;
for(it=ls[i].begin();it!=ls[i].end();++it)
{
int x=*it;
if(x==la) continue;
add(pc-1,x,inf),add(x,pc,inf);
la=x;
}
}
ans+=((dinic()-dt)/233)*2;
printf("%d\n",ans);
}
return 0;
}

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. 官网Windows 10安装程序驱动下载--截止:2019.01.06版本

    说明:鉴于win7,8不可直接再下载原装iOS文件,这份共享程序包是为以后N年做的准备.如果N年后这个包还可以用,就可以省去很多麻烦. 百度网盘分享:https://pan.baidu.com/s/1 ...

  2. 套接字I/O函数write/read writev/readv send/recv sendto/recvfrom sendmsg/recvmsg

    函数原型 read/write系原型 #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); #include ...

  3. MVC 小demo

    .field-validation-error { color: #f00; } .field-validation-valid { display: none; } .input-validatio ...

  4. centos7 php5.5 mongodb安装

    1.下载最新php MongoDB扩展源码 https://pecl.php.net/package/mongodb 最新的1.6不支持PHP5.5,得用老版本,1.5.5 wget https:// ...

  5. leetcode-easy-math-13 Roman to Integer

    mycode  97.21% class Solution(object): def romanToInt(self, s): """ :type s: str :rty ...

  6. CPU处理多任务——中断与轮询方式比较

    中断方式与轮询方式比较   中断的基本概念 程序中断通常简称中断,是指CPU在正常运行程序的过程中,由于预选安排或发生了各种随机的内部或外部事件,使CPU中断正在运行的程序,而转到为相应的服务程序去处 ...

  7. 在 Android 中进程的级别有哪些?

    a) Foreground processb) Visible processc) Service processd) Background processe) Empty process

  8. Windows监控——性能指标详解(转)

    http://blog.csdn.net/yiqin3399/article/details/51730106

  9. MYSQL5.5源码包编译安装

    MYSQL5.5源码安装首先安装必要的库yum -y install gcc*###### 安装 MYSQL ######首先安装camke 一.支持YUM,则yum install -y cmake ...

  10. iOS多选实现注意点

    下面对APP的多选选择列表实现进行总结,为了在以后的每个项目的多选实现,测试总是提一样的bug总结的. 具体的实现代码就不复制粘贴了,不过在多选问题上遇到问题的可以我们一起讨论讨论的哈... 可能总结 ...