\(\mathcal{Description}\)

  Link.

  (概括得说不清话了还是去看原题吧 qwq。

\(\mathcal{Solution}\)

  首先剔除回文串——它们一定对答案产生 \(1\) 的贡献。我们称一个句子是“正序”的,当且仅当句子的所有单词同时满足自己的字典序不小于翻转后的字典序;“逆序”则当且仅当句子的所有单词同时满足自己的字典序严格大于翻转后的字典序。从这条显眼的性质入手:

  此外观察者发现,对每一行(列)来说,按照确定后的阅读顺序读出的所有单词同时满足“自己的字典序不小于翻转后的字典序”,或同时满足“自己的字典序不大于翻转后的字典序”。

  也就是说任何一个句子都是“正序”或“逆序”的,而正序句子和正序句子同时选择不会对答案额外贡献,逆序句子亦然。所以答案一定是贡献自正逆序句子之间。考虑最小割黑白染色模型:

  在一张无向图中,钦定一些点为黑色,一些点为白色,为其余点染色,使得连接黑白两色的边数尽量少。

  接下来就自然而然了:

  • 源点 \(S\),汇点 \(T\),行 \(r_{1..n}\) 列 \(c_{1..m}\) 共 \(n+m\) 个点(为它们染色),每个正序单词对应 \(w_s,w_r\),表示其正序阅读和逆序阅读两种情况;
  • \(S\) 连向被钦定正序(注意需要用句子正序方向和阅读方向综合判断)的行/列结点,流量 \(+\infty\)(不可割);
  • 能正序读出 \(w\) 的所有行/列结点连向 \(w_s\),流量 \(+\infty\)(一旦有一个句子被正序读,\(w_s\) 就在字典中,不可割);
  • \(w_s\) 连向 \(w_r\),流量为 \(1\)(可以被割,对答案贡献 \(1\));
  • \(w_r\) 连向所有能逆序读出 \(w\) 的行/列结点,流量 \(+\infty\)(同理)。
  • \(T\) 同理 \(S\)。

  当然,单词去重。建图后跑最小割即可。复杂度 \(\mathcal O(\operatorname{Dinic}(n^2,n^3))\)(\(n,m\) 同阶)。

\(\mathcal{Code}\)

/* Clearink */

#include <map>
#include <set>
#include <queue>
#include <cstdio>
#include <vector> #define rep( i, l, r ) for ( int i = l, repEnd##i = r; i <= repEnd##i; ++i )
#define per( i, r, l ) for ( int i = r, repEnd##i = l; i >= repEnd##i; --i ) typedef unsigned long long ULL; const int MAXN = 80, INF = 0x3f3f3f3f;
const ULL BASE = 127;
int n, m, node, S, T;
int rdir[MAXN + 5], cdir[MAXN + 5];
int rcor[MAXN + 5], ccor[MAXN + 5];
char table[MAXN + 5][MAXN + 5];
std::set<ULL> parl;
std::map<ULL, int> virn; inline int imin ( const int a, const int b ) { return a < b ? a : b; } struct MaxFlowGraph {
static const int MAXND = 1e6, MAXEG = 1e6;
int ecnt, head[MAXND + 5], S, T, bound, curh[MAXND + 5], d[MAXND + 5];
struct Edge { int to, flow, nxt; } graph[MAXEG * 2 + 5]; inline MaxFlowGraph (): ecnt ( 1 ) {} inline void clear () {
ecnt = 1;
for ( int i = 0; i <= bound; ++i ) head[i] = 0;
} inline void restore () {
for ( int i = 2; i <= ecnt; i += 2 ) {
graph[i].flow += graph[i ^ 1].flow;
graph[i ^ 1].flow = 0;
}
} inline void link ( const int s, const int t, const int f ) {
graph[++ecnt].to = t, graph[ecnt].flow = f, graph[ecnt].nxt = head[s];
head[s] = ecnt;
} inline Edge& operator [] ( const int k ) { return graph[k]; } inline void operator () ( const int s, const int t, const int f ) {
#ifdef RYBY
printf ( "%d %d ", s, t );
if ( f == INF ) puts ( "INF" );
else printf ( "%d\n", f );
#endif
link ( s, t, f ), link ( t, s, 0 );
} inline bool bfs () {
static std::queue<int> que;
for ( int i = 0; i <= bound; ++i ) d[i] = -1;
d[S] = 0, que.push ( S );
while ( !que.empty () ) {
int u = que.front (); que.pop ();
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( graph[i].flow && !~d[v = graph[i].to] ) {
d[v] = d[u] + 1;
que.push ( v );
}
}
}
return ~d[T];
} inline int dfs ( const int u, const int iflow ) {
if ( u == T ) return iflow;
int ret = 0;
for ( int& i = curh[u], v; i; i = graph[i].nxt ) {
if ( graph[i].flow && d[v = graph[i].to] == d[u] + 1 ) {
int oflow = dfs ( v, imin ( iflow - ret, graph[i].flow ) );
ret += oflow, graph[i].flow -= oflow, graph[i ^ 1].flow += oflow;
if ( ret == iflow ) break;
}
}
if ( !ret ) d[u] = -1;
return ret;
} inline int calc ( const int s, const int t ) {
S = s, T = t;
int ret = 0;
for ( ; bfs (); ret += dfs ( S, INF ) ) {
for ( int i = 0; i <= bound; ++i ) curh[i] = head[i];
}
return ret;
}
} graph; struct Word {
std::vector<char> str;
inline int order () const {
if ( str.empty () ) return 0;
for ( int l = 0, r = str.size () - 1; ~r; ++l, --r ) {
if ( str[l] < str[r] ) return 1;
if ( str[r] < str[l] ) return -1;
}
return 0;
}
inline ULL hash () const {
if ( str.empty () ) return 0;
ULL ret = 0;
rep ( i, 0, str.size () - 1 ) ret = ret * BASE + ( str[i] - 'A' + 1 );
return ret;
}
inline ULL rhash () const {
if ( str.empty () ) return 0;
ULL ret = 0;
per ( i, str.size () - 1, 0 ) ret = ret * BASE + ( str[i] - 'A' + 1 );
return ret;
}
}; inline int virid ( const Word& tmp, const bool r ) {
ULL h = r ? tmp.rhash () : tmp.hash ();
if ( !virn.count ( h ) ) {
virn[h] = ++node;
graph ( node, node + 1, 1 );
return ++node - !r;
}
return virn[h] + r;
} inline void initCorDir () {
static Word tmp;
rep ( i, 1, n ) table[i][0] = table[i][m + 1] = '_';
rep ( i, 1, m ) table[0][i] = table[n + 1][i] = '_';
rep ( i, 1, n ) {
for ( int l = 1, r; l <= m; l = r + 1 ) {
tmp.str.clear ();
for ( r = l; table[i][r] != '_'; tmp.str.push_back ( table[i][r++] ) );
if ( l == r ) continue;
int type = tmp.order ();
if ( !type ) parl.insert ( tmp.hash () );
else {
rcor[i] = type;
int id;
if ( rcor[i] == 1 ) {
graph ( i, id = virid ( tmp, 0 ), INF );
graph ( id + 1, i, INF );
} else {
graph ( id = virid ( tmp, 1 ), i, INF );
graph ( i, id - 1, INF );
}
}
}
}
rep ( i, 1, m ) {
for ( int l = 1, r; l <= n; l = r + 1 ) {
tmp.str.clear ();
for ( r = l; table[r][i] != '_'; tmp.str.push_back ( table[r++][i] ) );
if ( l == r ) continue;
int type = tmp.order ();
if ( !type ) parl.insert ( tmp.hash () );
else {
ccor[i] = type;
int id;
if ( ccor[i] == 1 ) {
graph ( i + n, id = virid ( tmp, 0 ), INF );
graph ( id + 1, i + n, INF );
} else {
graph ( id = virid ( tmp, 1 ), i + n, INF );
graph ( i, id - 1, INF );
}
}
}
}
} inline void clear () {
/*
* node, rcor, ccor, graph are cleared.
* */
virn.clear (), parl.clear ();
} int main () {
// freopen ( "wall.in", "r", stdin );
// freopen ( "wall.out", "w", stdout );
int cas;
for ( scanf ( "%d", &cas ); cas--; ) {
clear ();
scanf ( "%d %d", &n, &m );
S = n + m + 1, T = node = S + 1;
rep ( i, 1, n ) scanf ( "%d", &rdir[i] ), rcor[i] = 0;
rep ( i, 1, m ) scanf ( "%d", &cdir[i] ), ccor[i] = 0;
rep ( i, 1, n ) scanf ( "%s", table[i] + 1 );
initCorDir ();
rep ( i, 1, n ) if ( rdir[i] ) {
if ( rcor[i] * rdir[i] >= 0 ) graph ( S, i, INF );
else graph ( i, T, INF );
}
rep ( i, 1, m ) if ( cdir[i] ) {
if ( ccor[i] * cdir[i] >= 0 ) graph ( S, i + n, INF );
else graph ( i + n, T, INF );
}
graph.bound = node;
int flw = graph.calc ( S, T );
printf ( "%d\n", flw * 2 + ( int ) parl.size () );
graph.clear ();
}
return 0;
}

Solution -「SDOI 2016」「洛谷 P4076」墙上的句子的更多相关文章

  1. 「区间DP」「洛谷P1043」数字游戏

    「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...

  2. Solution -「APIO 2016」「洛谷 P3643」划艇

    \(\mathcal{Description}\)   Link & 双倍经验.   给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...

  3. Solution -「JSOI 2019」「洛谷 P5334」节日庆典

    \(\mathscr{Description}\)   Link.   给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的).   \(|S|\le3\time ...

  4. Solution -「洛谷 P4372」Out of Sorts P

    \(\mathcal{Description}\)   OurOJ & 洛谷 P4372(几乎一致)   设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...

  5. Solution -「POI 2010」「洛谷 P3511」MOS-Bridges

    \(\mathcal{Description}\)   Link.(洛谷上这翻译真的一言难尽呐.   给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 ...

  6. 「洛谷4197」「BZOJ3545」peak【线段树合并】

    题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...

  7. 「洛谷3338」「ZJOI2014」力【FFT】

    题目链接 [BZOJ] [洛谷] 题解 首先我们需要对这个式子进行化简,否则对着这么大一坨东西只能暴力... \[F_i=\sum_{j<i} \frac{q_iq_j}{(i-j)^2}-\s ...

  8. 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】

    题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...

  9. 「洛谷3870」「TJOI2009」开关【线段树】

    题目链接 [洛谷] 题解 来做一下水题来掩饰ZJOI2019考炸的心情QwQ. 很明显可以线段树. 维护两个值,\(Lazy\)懒标记表示当前区间是否需要翻转,\(s\)表示区间还有多少灯是亮着的. ...

随机推荐

  1. Centos安装DenyHosts

    一.背景 个人申请的腾讯云主机被扫描端口,数百次登录失败的尝试 解决方法:安装工具,记录并屏蔽恶意访问. 二.检查环境 系统安装的sshd是否支持tcp_wrappers(默认都支持) ldd /us ...

  2. SYCOJ#111、吉祥物

    题目-吉祥物 (shiyancang.cn) 1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,x; 4 int pos(i ...

  3. phar反序列化

    我们一般利用反序列漏洞,一般都是借助unserialize()函数,不过随着人们安全的意识的提高这种漏洞利用越来越来难了,但是在今年8月份的Blackhat2018大会上,来自Secarma的安全研究 ...

  4. 利用栈实现括号匹配(python语言)

    原理: 右括号总是与最近的左括号匹配 --- 栈的后进先出 从左往右遍历字符串,遇到左括号就入栈,遇到右括号时,就出栈一个元素与其配对 当栈为空时,遇到右括号,则此右括号无与之匹配的左括号 当最终右括 ...

  5. redis实现简易在线聊天室

    redis_flask简易聊天室 项目构建 这时一个基于Redis数据库的简单小项目,使用redis缓存数据,并通过flask部署到浏览器,运行截图如下: 输入名字后,就可以登陆到聊天室,主要包括三个 ...

  6. C++基本面试题1

    #include<iostream>using namespace std;class A{public: A(char* s) :name(s), len(strlen(name.c_s ...

  7. [WAF攻防]从WAF攻防角度重看sql注入

    从WAF攻防角度重看sql注入 攻防都是在对抗中逐步提升的,所以如果想攻,且攻得明白,就必须对防有深刻的了解 sql注入的大体流程 Fuzz测试找到注入点 对注入点进行过滤检测,及WAF绕过 构建pa ...

  8. 学习Java第3天

    今天所做的工作: 1.循环结构 2.字符串 3.数组 4.面向对象概述 明天工作安排: 1.类和对象 2.包装类 所遇到的问题及解决方法: 1.循环foreach语句 2.字符串初始化与c++的差异 ...

  9. maven中profiles使用详解

    使用的场景 常常遇到一些项目中多环境切换的问题.比如在开发过程中用到开发环境,在测试中使用测试环境,在生产中用生产环境的情况.springboot中提供了 spring.profile.active的 ...

  10. Codeforces Round #746 Div. 2

    掉分快乐qwq C题代码以及分析(在注释里) /* * @Author: Nan97 * @Date: 2021-10-04 22:37:18 * @Last Modified by: Nan97 * ...