[SDOI2016]墙上的句子
题目描述
考古学家发现了一堵写有未知语言的白色墙壁,上面有一个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的边
这样跑一边最大流然后加上回文串就是答案了
代码
#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define ull unsigned long long
const int N = 80 ;
const int M = 3005 ;
const ull Base = 233 ;
const int INF = 1e8 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
char s[N][N] ;
int n , m , S , T ;
int ans , cnt , num = 1 ;
int dirh[N] , dirl[N] , hea[M] ;
int hnum[N] , lnum[N] , d[M] ;
int hw[2][N][N] , lw[2][N][N] , isbig[M] ;
ull pw[N] , hsh1[N] , hsh2[N] ;
map < ull , int > p , hap ;
struct E {
int nxt , to , dis ;
} edge[M * 20] ;
inline void Insert_edge(int from , int to , int dis) {
edge[++num].nxt = hea[from] ; edge[num].to = to ;
edge[num].dis = dis ; hea[from] = num ;
}
inline void add_edge(int u , int v , int w) {
Insert_edge(u , v , w) ;
Insert_edge(v , u , 0) ;
}
inline void Clear() {
ans = 0 ; cnt = 0 ; num = 1 ;
memset(isbig , false , sizeof(isbig)) ;
memset(hea , 0 , sizeof(hea)) ;
memset(hnum , 0 , sizeof(hnum)) ;
memset(lnum , 0 , sizeof(lnum)) ;
p.clear() ; hap.clear() ;
}
inline bool Bfs() {
queue < int > q ; q.push(S) ;
memset(d , 0 , sizeof(d)) ; d[S] = 1 ;
while(!q.empty()) {
int u = q.front() ; q.pop() ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ;
if(!d[v] && edge[i].dis > 0) {
d[v] = d[u] + 1 ;
q.push(v) ;
}
}
}
return d[T] ;
}
int Dfs(int u , int dis) {
if(u == T || !dis) return dis ;
int Sum = 0 ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ;
if(d[v] == d[u] + 1 && edge[i].dis > 0) {
int diss = Dfs(v , min(dis , edge[i].dis)) ;
if(diss > 0) {
edge[i].dis -= diss ; edge[i ^ 1].dis += diss ;
dis -= diss ; Sum += diss ; if(!dis) break ;
}
}
}
if(!Sum) d[u] = -1 ;
return Sum ;
}
inline void dinic() {
while(Bfs())
ans += Dfs(S , INF) ;
}
int main() {
int Case = read() ;
pw[0] = 1 ; for(int i = 1 ; i <= 75 ; i ++) pw[i] = pw[i - 1] * Base ;
while(Case --) {
Clear() ;
n = read() ; m = read() ;
for(int i = 1 ; i <= n ; i ++) dirh[i] = read() ;
for(int i = 1 ; i <= m ; i ++) dirl[i] = read() ;
for(int i = 1 ; i <= n ; i ++) {
scanf("%s",s[i] + 1) ;
for(int j = 1 ; j <= m ; j ++)
hsh1[j] = hsh1[j - 1] * Base + s[i][j] ;
for(int j = m ; j >= 1 ; j --)
hsh2[j] = hsh2[j + 1] * Base + s[i][j] ;
int lst = 1 ; ull tp1 , tp2 ;
for(int j = 1 , l , r ; j <= m ; j ++) {
if(s[i][j] == '_' || j == m) {
l = lst , r = j - 1 ; lst = j + 1 ;
if(s[i][j] != '_') ++ r ; if(l > r) continue ;
tp1 = hsh1[r] - hsh1[l - 1] * pw[r - l + 1] ;
tp2 = hsh2[l] - hsh2[r + 1] * pw[r - l + 1] ;
if(tp1 == tp2) {
if(hap[tp1]) continue ;
++ ans ; hap[tp1] = 1 ; continue ;
}
if(!p[tp1]) {
int vit1 = 1 ;
for(int k = 1 ; k <= r - l + 1 ; k ++) {
if(s[i][l + k - 1] > s[i][r - k + 1]) break ;
else if(s[i][l + k - 1] < s[i][r - k + 1]) {
vit1 = -1 ;
break ;
}
}
p[tp1] = ++ cnt ; isbig[cnt] = vit1 ;
p[tp2] = ++ cnt ; isbig[cnt] = - vit1 ;
}
hw[0][i][++hnum[i]] = p[tp1] ; hw[1][i][hnum[i]] = p[tp2] ;
}
}
}
for(int j = 1 ; j <= m ; j ++) {
for(int i = 1 ; i <= n ; i ++)
hsh1[i] = hsh1[i - 1] * Base + s[i][j] ;
for(int i = n ; i >= 1 ; i --)
hsh2[i] = hsh2[i + 1] * Base + s[i][j] ;
int lst = 1 ; ull tp1 , tp2 ;
for(int i = 1 , l , r ; i <= n ; i ++) {
if(s[i][j] == '_' || i == n) {
l = lst , r = i - 1 ; lst = i + 1 ;
if(s[i][j] != '_') ++ r ; if(l > r) continue ;
tp1 = hsh1[r] - hsh1[l - 1] * pw[r - l + 1] ;
tp2 = hsh2[l] - hsh2[r + 1] * pw[r - l + 1] ;
if(tp1 == tp2) {
if(hap[tp1]) continue ;
++ ans ; hap[tp1] = 1 ; continue ;
}
if(!p[tp1]) {
int vit1 = 1 ;
for(int k = 1 ; k <= r - l + 1 ; k ++) {
if(s[l + k - 1][j] > s[r - k + 1][j]) break ;
else if(s[l + k - 1][j] < s[r - k + 1][j]) {
vit1 = -1 ;
break ;
}
}
p[tp1] = ++ cnt ; isbig[cnt] = vit1 ;
p[tp2] = ++ cnt ; isbig[cnt] = -vit1 ;
}
lw[0][j][++lnum[j]] = p[tp1] ; lw[1][j][lnum[j]] = p[tp2] ;
}
}
}
S = 0 , T = cnt + n + m + 1 ;
for(int i = 1 , x , y ; i <= cnt ; i += 2) {
x = i ; y = i + 1 ;
if(isbig[x] == 1) swap(x , y) ;
add_edge(x , y , 2) ;
}
for(int i = 1 ; i <= n ; i ++) {
if(!hnum[i]) continue ;
if((dirh[i] == 1 && isbig[hw[0][i][1]] < 0) || (dirh[i] == -1 && isbig[hw[1][i][1]] < 0)) {
add_edge(S , cnt + i , INF) ;
for(int j = 1 ; j <= hnum[i] ; j ++) {
if(dirh[i] == 1) add_edge(cnt + i , hw[0][i][j] , INF) ;
else add_edge(cnt + i , hw[1][i][j] , INF) ;
}
}
else if((dirh[i] == 1 && isbig[hw[0][i][1]] > 0) || (dirh[i] == -1 && isbig[hw[1][i][1]] > 0)) {
add_edge(cnt + i , T , INF) ;
for(int j = 1 ; j <= hnum[i] ; j ++) {
if(dirh[i] == 1) add_edge(hw[0][i][j] , cnt + i , INF) ;
else add_edge(hw[1][i][j] , cnt + i , INF) ;
}
}
else {
for(int j = 1 , x , y ; j <= hnum[i] ; j ++) {
x = hw[0][i][j] , y = hw[1][i][j] ;
if(isbig[x] > 0) swap(x , y) ;
add_edge(cnt + i , x , INF) ;
add_edge(y , cnt + i , INF) ;
}
}
}
for(int i = 1 ; i <= m ; i ++) {
if(!lnum[i]) continue ;
if((dirl[i] == 1 && isbig[lw[0][i][1]] <= 0) || (dirl[i] == -1 && isbig[lw[1][i][1]] <= 0)) {
add_edge(S , cnt + n + i , INF) ;
for(int j = 1 ; j <= lnum[i] ; j ++) {
if(dirl[i] == 1) add_edge(cnt + n + i , lw[0][i][j] , INF) ;
else add_edge(cnt + n + i , lw[1][i][j] , INF) ;
}
}
else if((dirl[i] == 1 && isbig[lw[0][i][1]] >= 0) || (dirl[i] == -1 && isbig[lw[1][i][1]] >= 0)) {
add_edge(cnt + n + i , T , INF) ;
for(int j = 1 ; j <= lnum[i] ; j ++) {
if(dirl[i] == 1) add_edge(lw[0][i][j] , cnt + n + i , INF) ;
else add_edge(lw[1][i][j] , cnt + n + i , INF) ;
}
}
else {
for(int j = 1 , x , y ; j <= lnum[i] ; j ++) {
x = lw[0][i][j] , y = lw[1][i][j] ;
if(isbig[x] > 0) swap(x , y) ;
add_edge(cnt + n + i , x , INF) ;
add_edge(y , cnt + n + i , INF) ;
}
}
}
dinic() ;
printf("%d\n",ans) ;
}
return 0 ;
}