题解 P4076 【[SDOI2016]墙上的句子】
题目大意
给出
保证对于同一个字符串内的单词 , 要么所有单词的正串字典序大于等于反串 , 要么所有单词的反串字典序大于等于正串.
solution
回文串一定产生
看到正反可以联想到 文理分科 , 类比这道题考虑最小割做法
一开始是想着直接将
转换思维 查阅题解 , 将单词作为被划分集合的对象
设源点为
将一个单词分为正串与反串两个节点 ( 这里正串定义为正反串中字典序较小的串 ) , 正串向反串连容量为
正串被划分到
正串被划分到
反串被划分到
反串被划分到
当正串一定出现时 ,
当反串一定出现时 , 反串向
接下来考虑那些没有确定读法的字符串
这里有一个重要条件
对于同一个字符串内的单词 , 要么所有单词的正串字典序大于等于反串 , 要么所有单词的反串字典序大于等于正串.
意味着一个字符串中所有出现的单词都是正串 , 或者是反串 ( 这里假设都是正串 )
设使得字符串中单词都是正串的读法是将字符串划入
字符串被划入
字符串被划入
若字符串被划入
若字符串被划入
code
#include <bits/stdc++.h>
using namespace std;
const int N = 7e2 + 5;
const int M = N * N;
const int INF = 0x3f3f3f3f;
int _w;
int head[N] , eidx , dep[N] , que[N] , he , ta , cur[N] , st , ed;
struct Edge { int nxt , to , flow , cap; } edge[M];
void addedge( int u , int v , int cap ) {
edge[++eidx] = (Edge) { head[u] , v , 0 , cap }; head[u] = eidx;
edge[++eidx] = (Edge) { head[v] , u , 0 , 0 }; head[v] = eidx;
}
bool bfs( void ) {
static int u;
memset( dep , 0 , sizeof dep );
dep[que[he = ta = 1] = st] = 1;
while( he <= ta ) {
u = que[he++];
for( int i = head[u] , v ; ~i ; i = edge[i].nxt )
if( !dep[v = edge[i].to] && edge[i].cap > edge[i].flow )
dep[que[++ta] = v] = dep[u] + 1;
}
return dep[ed] != 0;
}
int dfs( int u , int fl ) {
if( u == ed || !fl ) return fl;
int g = 0 , d;
for( int & i = cur[u] ; ~i ; i = edge[i].nxt )
if( dep[edge[i].to] == dep[u] + 1 && ( d = dfs( edge[i].to , min( edge[i].cap - edge[i].flow , fl - g ) ) ) ) {
edge[i].flow += d , edge[i ^ 1].flow -= d;
if( ( g += d ) >= fl ) break;
}
return g;
}
int dinic( void ) {
int res = 0;
while( bfs() ) {
memcpy( cur , head , sizeof head );
res += dfs( st , INF );
}
return res;
}
int n , m , col[N] , row[N] , idx;
char str[N][N];
set<string> pal;
map<string,int> id;
void analyze( string s , int type , int u ) {
static int n;
static string a , b;
n = s.length();
for( int i = 0 , r , flag ; i < n ; i = r + 1 ) {
r = i;
if( s[i] == '_' ) continue;
while( r < n - 1 && s[r + 1] != '_' ) ++r;
a = s.substr( i , r - i + 1 );
b = a; reverse( b.begin() , b.end() );
if( b < a ) swap( a , b ) , flag = -1;
else flag = 1;
if( a == b ) {
pal.insert( a );
continue;
}
if( !id.count( a ) ) {
id[a] = ++idx;
id[b] = ++idx;
addedge( idx - 1 , idx , 2 );
} flag *= type;
if( flag == 1 ) addedge( st , id[a] , INF );
else if( flag == -1 ) addedge( id[b] , ed , INF );
else addedge( id[b] , u , INF ) , addedge( u , id[a] , INF );
}
}
void solve( void ) {
_w = scanf("%d%d",&n,&m); idx = n + m;
memset( head , -1 , sizeof head ) , eidx = -1;
id.clear() , pal.clear();
for( int i = 1 ; i <= n ; ++i ) _w = scanf("%d",row + i );
for( int i = 1 ; i <= m ; ++i ) _w = scanf("%d",col + i );
for( int i = 1 ; i <= n ; ++i ) _w = scanf("%s",str[i]+1);
st = 0 , ed = N - 1;
static string tmp;
for( int i = 1 ; i <= n ; ++i ) {
tmp = str[i] + 1;
analyze( tmp , row[i] , i );
}
for( int i = 1 ; i <= m ; ++i ) {
tmp = "";
for( int j = 1 ; j <= n ; ++j )
tmp += str[j][i];
analyze( tmp , col[i] , i + n );
}
printf("%d\n",dinic() + int( pal.size() ) );
}
int main( void ) {
int T;
_w = scanf("%d",&T);
while( T-- ) solve();
return 0;
}