luogu P4076 [SDOI2016]墙上的句子
题意看了我半天(逃 (应该是我语文太差了)
题意是要确定每一行和每一列的看单词的顺序,使得同时正着出现和反着出现在里面的单词数量最少,每行和每列的性质是这一行所有单词反过来的单词要么字典序大于等于原来的,要么小于等于原来的
首先回文单词一定会出现,可以直接加入答案,以后就不用管了.对于剩下的单词,如果不想让他贡献答案,那就要使的每次读这个单词都是一个样子,例如"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]墙上的句子的更多相关文章
- [SDOI2016]墙上的句子
题目描述 考古学家发现了一堵写有未知语言的白色墙壁,上面有一个n行m列的格子,其中有些格子内被填入了某个A至Z的大写字母,还有些格子是空白的. 一直横着或竖着的连续若干个字母会形成一个单词,且每一行的 ...
- Solution -「SDOI 2016」「洛谷 P4076」墙上的句子
\(\mathcal{Description}\) Link. (概括得说不清话了还是去看原题吧 qwq. \(\mathcal{Solution}\) 首先剔除回文串--它们一定对答案产 ...
- 【LOJ】#2066. 「SDOI2016」墙上的句子
题解 我一直也不会网络流--orz 我们分析下这道题,显然和行列没啥关系,就是想给你n + m个串 那么我们对于非回文单词之外的单词,找到两两匹配的反转单词(即使另一个反转单词不会出现也要建出来) 具 ...
- 【题解】Luogu P4069 [SDOI2016]游戏
原题传送门 看到这种题,想都不用想,先写一个树链剖分 然后发现修改操作增加的是等差数列,这使我们想到了李超线段树 先进性树剖,然后用李超线段树维护区间最小,这样就做完了(写码很容易出错) 复杂度为\( ...
- Luogu P4070 [SDOI2016]生成魔咒
题目链接 \(Click\) \(Here\) 其实是看后缀数组资料看到这个题目的,但是一眼反应显然后缀自动机,每次维护添加节点后的答案贡献即可,唯一不友好的一点是需要平衡树维护,这里因为复杂度不卡而 ...
- Luogu P4071 [SDOI2016]排列计数
晚上XZTdalao给我推荐了这道数论题.太棒了又可以A一道省选题了 其实这道题也就考一个错排公式+组合数+乘法逆元 我们来一步一步分析 错排公式 通俗的说就是把n个1~n的数排成一个序列A,并使得所 ...
- Luogu 4069 [SDOI2016]游戏
BZOJ 4515 树链剖分 + 李超线段树 要求支持区间插入一条线段,然后查询一个区间内的最小值.可以使用李超线段树解决,因为要维护一个区间内的最小值,所以每一个结点再维护一个$res$表示这个区间 ...
- Luogu P4068 [SDOI2016]数字配对
反正现在做题那么少就争取做一题写一题博客吧 看到题目发现数字种类不多,而且结合价值的要求可以容易地想到使用费用流 但是我们如果朴素地建图就会遇到一个问题,若\(i,j\)符合要求,那么给\(i,j\) ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
随机推荐
- linux下安装R第三方包forecast
ERROR: [root@localhost soft]# R CMD INSTALL curl_3.1.tar.gz WARNING: ignoring environment value of R ...
- Cassandra 如何处理跨数据中心的数据库延时问题
分布式系统的可靠.延时.一致性等问题是一般性问题,不局限于数据库,而Cassandra提供了一个很好的解决思路. Cassandra号称能做到跨数据中心的数据库访问的高效访问,它的实现方式其实是把延时 ...
- P3956 棋盘
P3956 棋盘 题解 注释都在代码里了 这道题可以用DFS做,记忆化搜索,维护一个money[ ][ ] 表示到达当前节点的最小花费 不需要记录VIS,因为有一个最小值判断,如果走重复的话一定会得到 ...
- 详析静态网站与动态网站区别(服务器ip dns 端口)
几个基本知识 1.服务器:能够提供服务的机器 取决于机器上所安装的服务软件 1) web服务器:提供web服务(网站访问) 就需要安装web服务软件,Apache,tomcat,iis等 2. ...
- 系统分析与设计HW9
使用 ECB 实现 make reservation 用例的详细设计(包含用例简介,顺序图,类图) 用例简介: 搜索酒店 1.1 选择城市 1.2 选择日期 生成订单 2.1 选择酒店 2.2 选择日 ...
- Closure - Mimicking block scope
The basic syntax of an anoymous function used as a block scope (often called a private scope) is as ...
- ubuntu登录SYSU-SECURE
1. 选中SYSU-SECURE网络连接. 2. 点击进入Wi-Fi 安全性选项卡. 3. 修改[认证(T)]项的值为[受保护的EAP(PEAP)]. 4. 连接成功.
- WebApi使用二进制方式上传和下载文件
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; usi ...
- 关机命令 shutdown
参考资料:[http://jingyan.baidu.com/article/49ad8bce705f3f5834d8faec.html]
- linux建立ftp用户
#!/bin/bash sleep 1 mkdir -p /ceshi/ userdel ceshi useradd -d /ceshi -s /sbin/nologin ceshi echo mim ...