Solution -「SDOI 2016」「洛谷 P4076」墙上的句子
\(\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」墙上的句子的更多相关文章
- 「区间DP」「洛谷P1043」数字游戏
「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...
- Solution -「APIO 2016」「洛谷 P3643」划艇
\(\mathcal{Description}\) Link & 双倍经验. 给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...
- Solution -「JSOI 2019」「洛谷 P5334」节日庆典
\(\mathscr{Description}\) Link. 给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的). \(|S|\le3\time ...
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- Solution -「POI 2010」「洛谷 P3511」MOS-Bridges
\(\mathcal{Description}\) Link.(洛谷上这翻译真的一言难尽呐. 给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 ...
- 「洛谷4197」「BZOJ3545」peak【线段树合并】
题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...
- 「洛谷3338」「ZJOI2014」力【FFT】
题目链接 [BZOJ] [洛谷] 题解 首先我们需要对这个式子进行化简,否则对着这么大一坨东西只能暴力... \[F_i=\sum_{j<i} \frac{q_iq_j}{(i-j)^2}-\s ...
- 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】
题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...
- 「洛谷3870」「TJOI2009」开关【线段树】
题目链接 [洛谷] 题解 来做一下水题来掩饰ZJOI2019考炸的心情QwQ. 很明显可以线段树. 维护两个值,\(Lazy\)懒标记表示当前区间是否需要翻转,\(s\)表示区间还有多少灯是亮着的. ...
随机推荐
- HDOJ1573X问题
https://acm.hdu.edu.cn/showproblem.php?pid=1573 n组线性同余方程求解,最后求出多少解.而最终的解的周期为最小公倍数,范围内的,需要这样算.如果最小超过, ...
- 使用.NET 6开发TodoList应用(25)——实现RefreshToken
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 在上一篇文章使用.NET 6开发TodoList应用(24)--实现基于JWT的Identity功能中,我们演示了如何使用.N ...
- 硬核 - Java 随机数相关 API 的演进与思考(下)
本系列将 Java 17 之前的随机数 API 以及 Java 17 之后的统一 API 都做了比较详细的说明,并且将随机数的特性以及实现思路也做了一些简单的分析,帮助大家明白为何会有这么多的随机数算 ...
- 信息收集&Fuzz
本文译自https://0xjoyghosh.medium.com/information-gathering-scanning-for-sensitive-information-reloaded- ...
- [STM32F10x] 从零开始创建一个基于标准库的工程
硬件:STM32F103C8T6 平台:MDK-AMR V4.70 1.创建一个Keil uVision 的工程 要点:相同类型的源文件放在一起以便于管理 2.添加标准库源文件 3.添加几 ...
- 云计算——实验一 HDFS与MAPREDUCE操作
1.虚拟机集群搭建部署hadoop 利用VMware.centOS-7.Xshell(secureCrt)等软件搭建集群部署hadoop 远程连接工具使用Xshell: HDFS文件操作 2.1 HD ...
- 146_LRU cache | LRU缓存设计
题目: Design and implement a data structure for Least Recently Used (LRU) cache. It should support the ...
- python11day
昨日回顾 函数的参数: 实参角度:位置参数.关键字参数.混合参数 形参角度:位置参数.默认参数.仅限关键字参数.万能参数 形参角度参数顺序:位置参数,*args,默认参数,仅限关键字参数,**kwar ...
- MySQL更新数据时,日志(redo log、binlog)执行流程
1:背景 项目需要做Es和数据库的同步,而手动在代码中进行数据同步又是Es的一些不必要的数据同步操作和业务逻辑耦合,所以使用的了读取mysql的binlog日志的方式进行同步Es的数据. 问题1:根据 ...
- CSS之常见布局|常用单位|水平垂直居中
常见布局: 1. 流式布局:百分比布局,宽高.margin.pinding都是百分比 2. 固定布局:盒子的宽高固定,如:margin.padding等 3. 浮动布局:float 4. 弹性布局:f ...