\(\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. SQL高级优化系列

    目录 SQL高级优化系列(一)之MySQL优化 SQL高级优化系列(二)之MySQL架构 SQL高级优化系列(三)之存储引擎 SQL高级优化系列(四)之SQL优化 SQL高级优化系列(五)之执行计划 ...

  2. spring boot 中使用swagger

    一.pom.xml <dependency> <groupId>io.springfox</groupId> <artifactId>springfox ...

  3. JavaScript的执行过程(深入执行上下文、GO、AO、VO和VE等概念)

    JavaScript的执行过程 前言 编写一段JavaScript代码,它是如何执行的呢?简单来说,JS引擎在执行JavaScript代码的过程中需要先解析再执行.那么在解析阶段JS引擎又会进行哪些操 ...

  4. Flink 非对齐Unaligned的checkpoint(源码分析)

    本文源码基于flink1.14 在帮助用户排查任务的时候,经常会发现部分task处理的慢,在Exactly once语义时需要等待快照的对齐而白白柱塞的情况 在flink1.11版本引入了非对齐的ch ...

  5. 知乎上一个关于Android面试的问题答案

    由于链接出错,这里附上原文链接:Touch Me 前段时间面试,自己以及小伙伴们简要的汇总的一些面试问题,可以对照的参考一下吧- 建议就是在面一家公司之前了解好这个公司的app是以什么为驱动的,例如电 ...

  6. [源码分析] Facebook如何训练超大模型 --- (2)

    [源码分析] Facebook如何训练超大模型 --- (2) 目录 [源码分析] Facebook如何训练超大模型 --- (2) 0x00 摘要 0x01 回顾 1.1 ZeRO 1.1.1 Ze ...

  7. 根据happens-before法则借助同步

    在文章的开始,我们先来看一段代码以及他的执行情况: public class PossibleRecording{ static int x = 0, y = 0; static int a = 0, ...

  8. 走进Task(1):什么是Task

    目录 前言 从表象讲起 Task 从何而来 Task 常见用法 Task 的分类 按是否包含 Result 分,也就是是否是泛型 Task 按得到 Task 的方式,可以分为 对 Task 进行分解 ...

  9. 将Cesium ion上的3D Tiles和Bing imagery应用到osgEarth

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Pelican Mapping 激动的宣布支持加载Cesium ...

  10. 春节将至,喜庆的烟花安排上(js实现烟花)

    一年一度的春节即将来临,然后苦逼的我还在使劲的摸鱼,未能回家体验小时候路边放爆竹的快乐时光,所以只能在网上来实现这个小小的心愿了.烟花静态效果图如下: 为了大伙复制方便就不分开写,直接复制即可,具体实 ...