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\)表示区间还有多少灯是亮着的. ...
随机推荐
- Spark案例练习-打包提交
关注公众号:分享电脑学习回复"百度云盘" 可以免费获取所有学习文档的代码(不定期更新)云盘目录说明:tools目录是安装包res 目录是每一个课件对应的代码和资源等doc 目录是一 ...
- vue实现PC端分辨率适配
lib-flexible + px2rem Loader lib-flexible 阿里伸缩布局方案 px2rem-loader:px转rem: 依赖 首先需要安装 vue-cli 脚手架,这里我安装 ...
- 阿里云服务器ECS Ubuntu16.04 初次使用配置教程(图形界面安装)
原文链接:? 传送门 前一阵子购买了阿里云的云服务器ECS(学生优惠),折腾了一阵子后对有些东西不太满意,所以就重新初始化了磁盘,刚好要重新安装图形界面,于是就顺手写了这么一篇文章. 第一次登陆服务器 ...
- 记一次 .NET 某消防物联网 后台服务 内存泄漏分析
一:背景 1. 讲故事 去年十月份有位朋友从微信找到我,说他的程序内存要炸掉了...截图如下: 时间有点久,图片都被清理了,不过有点讽刺的是,自己的程序本身就是做监控的,结果自己出了问题,太尴尬了 二 ...
- 纯手画WinForm的Alert提示弹出框
纯手画WinForm的Alert弹框 一.窗体设置 设置以下属性: 属性名 属性值 说明 AutoScaleMode None 确定屏幕分辨率或字体更改时窗体如何缩放 BackColor 103, 1 ...
- MATLAB中插值算法实现
%%%1.M文件%(1).以往少的程序可以在命令行窗口进行编码,但大量的程序编排到命令行窗口,%会有造成乱码的危险.(2).如果将命令编成程序存储在一个文件中(M文件),依次运行文件中的命令,则可以重 ...
- RHCSA 第二天
1.Linux中的文件类型以及符号的表示 (1) 普通文件: 使用 ls -l 命令后,第一列第一个字符为 "-" 的文件为普通文件,如上图所示,普通文件一般为灰色字体,绿色字体的 ...
- 【刷题-LeetCode】205. Isomorphic Strings
Isomorphic Strings Given two strings *s* and *t*, determine if they are isomorphic. Two strings are ...
- 【解决了一个小问题】go.mod文件中引用另一个库,总会自动拉取新版本
我的项目依赖某个旧的公共库: require ( git.xxx.com/myprj/mylib v0.0.43 ) 可以编译的时候,系统总会自动加上这样的路径: require ( git.xxx. ...
- 集合框架-LinkedList集合练习(堆栈和队列)
1 package cn.itcast.p2.linkedlist.test; 2 3 import java.util.LinkedList; 4 5 /* 6 * 请使用LinkedList来模拟 ...