题目传送门:LOJ #3049。

题意简述:

给定一个长度为 \(n\) 的母串 \(S\)。

有 \(n_a\) 个 A 类串,都是 \(S\) 的子串,以区间的形式给出。

有 \(n_b\) 个 B 类串,都是 \(S\) 的子串,以区间的形式给出。

有 \(m\) 个支配关系,形式为第 \(i\) 个 A 类串支配第 \(j\) 个 B 类串。

你需要求出最长的字符串 \(T\) 的长度,使得 \(T\) 可以被划分为若干个 A 类串的拼接,并且相邻两个 A 类串 \(t_i\) 和 \(t_{i+1}\) 满足 \(t_i\) 支配某个 B 类串 \(j\),而 \(j\) 是 \(t_{i+1}\) 的前缀。

如果 \(T\) 可以无限长,输出 -1

题解:

如果我们设法从每个 B 类串向存在前缀为这个 B 类串的 A 类串连边,则显然若有环则答案可以无限大,若无环则可以做一遍 DAG DP 得到答案。

然而我们并不能直接实现这一过程,且不论能否快速确定前缀从属关系,这类边的条数就能达到 \(\mathcal{O}(n_an_b)\) 级别。

当边数太多时,首先应该想到的就是数据结构优化建边,典型的例子是树状结构优化建边,例如线段树和后缀树。

实际上这题可以直接使用后缀树优化建边,边数只有线性级别,但是过程还是 \(\mathcal{O}(n\log n)\) 的。至于建后缀树可以直接建也可以对反串建 SAM。

对于我这种不会后缀树,SAM 理解也不深的菜兔,还是选择了后缀数组。

首先建出后缀数组,这个大家都会。然后对每个 B 类串考虑如何连边。

假设我们已经对每个 A 类和 B 类串在母串上定位了,那么包含某个 B 类串作为前缀的 A 类串需要满足两个条件:
该 A 串的左端点对应的后缀和 B 串的左端点对应的后缀的 LCP 长度应该大于 B 串的长度,并且该 A 串的长度也应该大于 B 串的长度。

第一个条件可以转化为对应着后缀数组上的一段区间,具体根据 Height 数组确定,这一步建出 ST 表后二分可以在 \(\mathcal{O}(\log n)\) 的时间内解决。

第二个条件似乎没有什么巧妙的办法。不过我们可以考虑按照长度从大到小的顺序加入每个串,这样就能够扔掉第二个限制了。

所以具体的思路就是按照长度从大到小排序插入 A 类串,在 B 类串和数据结构之间建边,使用合适的数据结构维护历史版本和区间。

很显然,主席树便是合适的数据结构,主席树优化建边,树内边数 \(\mathcal{O}(n_a\log n)\),树外边数 \(\mathcal{O}(n_b\log n)\)。

接下来是代码,复杂度 \(\mathcal{O}((n+n_a+n_b)\log n)\):

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>typedef long long LL;
const int MN = 200005;
const int MS = 4200005;char str[MN];
int N, Sig, rk[MN], SA[MN], SA2[MN], buk[MN], tmp[MN];
int Height[MN];
inline void getHeight() {for (int i = 1, k = 0; i <= N; ++i) {if (rk[i] == 1) { Height[rk[i]] = k = 0; continue; }if (k) --k;int j = SA[rk[i] - 1];while (i + k <= N && j + k <= N && str[i + k] == str[j + k]) ++k;Height[rk[i]] = k;}
}
inline void RSort() {for (int i = 1; i <= Sig; ++i) buk[i] = 0;for (int i = 1; i <= N; ++i) ++buk[rk[i]];for (int i = 1; i <= Sig; ++i) buk[i] += buk[i - 1];for (int i = N; i >= 1; --i) SA[buk[rk[SA2[i]]]--] = SA2[i];
}
inline void getSA(char *str) {Sig = 127, rk[N + 1] = 0;for (int i = 1; i <= N; ++i) rk[i] = str[i], SA2[i] = i;RSort();for (int j = 1; j <= N; j <<= 1) {int p = 0;for (int i = N - j + 1; i <= N; ++i) SA2[++p] = i;for (int i = 1; i <= N; ++i) if (SA[i] > j) SA2[++p] = SA[i] - j;RSort();tmp[SA[1]] = p = 1;for (int i = 2; i <= N; ++i) {int lst = SA[i - 1], now = SA[i];if (rk[lst] != rk[now] || rk[lst + j] != rk[now + j]) ++p;tmp[SA[i]] = p;}for (int i = 1; i <= N; ++i) rk[i] = tmp[i];if ((Sig = p) == N) break;}getHeight();
}int Lg[MN];
inline void Log(int N) {Lg[0] = -1;for (int i = 1; i <= N; ++i) Lg[i] = Lg[i >> 1] + 1;
}
int ST[MN][18];
inline void InitST() {for (int i = 2; i <= N; ++i) ST[i][0] = Height[i];for (int j = 1; j <= Lg[N - 1]; ++j) {for (int i = 1; i <= 1 << j; ++i) ST[i][j] = 0;for (int i = 1 << j | 1; i <= N; ++i)ST[i][j] = std::min(ST[i - (1 << (j - 1))][j - 1], ST[i][j - 1]);}
}int NA, la[MN], ra[MN];
int NB, lb[MN], rb[MN];
struct sub {int lb, len, typ, id;sub() {}sub(int lb, int len, int typ, int id) : lb(lb), len(len), typ(typ), id(id) {}inline friend bool operator <(sub i, sub j) {return i.len == j.len ? i.typ < j.typ : i.len > j.len;}
} substrs[MN * 2];int d[MS];
std::vector<int> G[MS];
inline void addEdge(int x, int y) { ++d[y]; G[x].push_back(y); }int rt[MN], lc[MS], rc[MS], wgh[MS], cnt;
void Mdf(int &rt, int l, int r, int p, int x) {lc[++cnt] = lc[rt], rc[cnt] = rc[rt];if (rt) addEdge(cnt, rt);wgh[rt = cnt] = 0;if (l == r) { addEdge(rt, x); return ; }int mid = (l + r) >> 1;if (p <= mid) Mdf(lc[rt], l, mid, p, x), addEdge(rt, lc[rt]);else Mdf(rc[rt], mid + 1, r, p, x), addEdge(rt, rc[rt]);
}
void Edg(int rt, int l, int r, int a, int b, int x) {if (!rt || r < a || b < l) return ;if (a <= l && r <= b) { addEdge(x, rt); return ; }int mid = (l + r) >> 1;Edg(lc[rt], l, mid, a, b, x);Edg(rc[rt], mid + 1, r, a, b, x);
}int que[MS], l, r;
LL f[MS];int main() {int T; scanf("%d", &T);Log(200000);while (T--) {scanf("%s", str + 1);N = strlen(str + 1);getSA(str);InitST();scanf("%d", &NA);for (int i = 1; i <= NA; ++i)scanf("%d%d", &la[i], &ra[i]),substrs[i] = sub(rk[la[i]], ra[i] - la[i] + 1, 0, i);scanf("%d", &NB);for (int i = 1; i <= NB; ++i)scanf("%d%d", &lb[i], &rb[i]),substrs[NA + i] = sub(rk[lb[i]], rb[i] - lb[i] + 1, 1, i);std::sort(substrs + 1, substrs + NA + NB + 1);cnt = NA + NB;for (int i = 1; i <= NA; ++i) wgh[i] = ra[i] - la[i] + 1;for (int i = 1; i <= NB; ++i) wgh[NA + i] = 0;for (int i = 1, gen = 0; i <= NA + NB; ++i) {sub p = substrs[i];if (!p.typ) ++gen, Mdf(rt[gen] = rt[gen - 1], 1, N, p.lb, p.id);else {int Lb = p.lb, Rb = p.lb;for (int j = Lg[p.lb - 1]; ~j; --j)if (ST[Lb][j] >= p.len) Lb -= 1 << j;for (int j = Lg[N - p.lb]; ~j; --j)if (Rb + (1 << j) <= N && ST[Rb + (1 << j)][j] >= p.len) Rb += 1 << j;Edg(rt[gen], 1, N, Lb, Rb, NA + p.id);}}int M; scanf("%d", &M);for (int i, j; M--; ) {scanf("%d%d", &i, &j);addEdge(i, NA + j);}LL Ans = 0;l = 1, r = 0;for (int i = 1; i <= cnt; ++i) {f[i] = wgh[i];if (!d[i]) que[++r] = i;}while (l <= r) {int u = que[l++];Ans = std::max(Ans, f[u]);for (auto v : G[u]) {f[v] = std::max(f[v], f[u] + wgh[v]);if (!--d[v]) que[++r] = v;}}if (r != cnt) puts("-1");else printf("%lld\n", Ans);for (int i = 1; i <= cnt; ++i) d[i] = 0, G[i].clear();}return 0;
}

实际上,同样是后缀数组预处理,关于建边有更好的方法,比如用后缀数组建出后缀树,然后在后缀树上乱搞建边,这样是线性的。

转载于:https://www.cnblogs.com/PinkRabbit/p/SHOI2019D1T2.html

LOJ 3049: 洛谷 P5284: 「十二省联考 2019」字符串问题相关推荐

  1. 「十二省联考 2019」皮配——dp

    题目 [题目描述] #### 题目背景 一年一度的综艺节目<中国好码农>又开始了.本季度,好码农由 Yazid.Zayid.小 R.大 R 四位梦想导师坐镇,他们都将组建自己的梦想战队,并 ...

  2. 【洛谷5284】[十二省联考2019] 字符串问题(后缀树优化建边)

    题目: 洛谷 5284 分析: 首先不要问我标题里的「后缀树」是什么,我也不会,瞎写的 -- (传说就是反串后缀自动机的 fa 树?) 前置技能:[知识总结]后缀自动机的构建 首先有一个很 naive ...

  3. 「十二省联考 2019」希望

    Solution 题意简述:选出 k k k 个树上连通块,使得存在一个点 u u u 满足: 1. u u u 在这 k k k 个连通块的交集之中. 2.对于这 k k k 个连通块中的任意一点 ...

  4. 【十二省联考2019】字符串问题【后缀自动机】【拓扑排序】

    题意:给一个字符串 SSS,以子串的形式给出一些 A 类串和 B 类串以及 mmm 对 A 类串支配 B 类串的关系.求一个总长度最长的 A 类串序列,使得每个串都存在一个 B 类串前缀被后一个串支配 ...

  5. 【BZOJ5498】[十二省联考2019]皮配(动态规划)

    [BZOJ5498][十二省联考2019]皮配(动态规划) 题面 BZOJ 洛谷 题解 先考虑暴力\(dp\),设\(f[i][j][k]\)表示前\(i\)所学校,有\(j\)人在某个阵营,有\(k ...

  6. [十二省联考2019]春节十二响——长链剖分+堆

    题目链接: [十二省联考2019]春节十二响 可以发现每条链上的所有点都要放在不同的段里,那么最多只需要树的深度这么多段就够了. 因为这样可以保证每条链上的点可以放在不同的段中而且一个点放在这些段中一 ...

  7. 十二省联考 2019 题解

    [十二省联考2019]异或粽子 首先异或转前缀和,类似超级钢琴,将三元组 ( l , r , p ) (l,r,p) (l,r,p) 插入堆,表示 s u m [ p ] sum[p] sum[p] ...

  8. 【十二省联考2019】春节十二响

    题面 https://www.luogu.org/problem/P5290 题解 真的是我傻逼,十二省联考$day2$至今还是我的噩梦.$day1$起码一直在调可持久化$trie$树,$day2$真 ...

  9. HAOI(十二省联考)2019 qwq记

    \(\large{Day\ -1}:\) 放假了,白天大概是抱着最后一次在机房的心态复习着板子过去的.看着机房里的各位神仙丝毫不慌的颓倒是有点慌了,敲了一下多项式的板子感觉写的相当自闭,感觉AFO应该 ...

最新文章

  1. thinkJava@第五章@隐藏实施过程
  2. 【西交ACM】298 第N大的数
  3. Unity3D中常用的数据结构 学习
  4. 2015年第六届蓝桥杯 - 省赛 - C/C++大学B组 - A. 奖券数目
  5. codeforces contest 1140(D~G)
  6. php ip处理函数,PHP取ip地址函数
  7. python网络库_python的网络库
  8. 为什么有科学家怀疑太阳系是被设计出来的
  9. 获取Android 光感Sensor的值
  10. mount error(12): Cannot allocate memory解决办法
  11. Java使用OpenOffice实现Office系列文件转Pdf
  12. 2022全新版千月影视源码原生播放器 投屏 选集 下载应有尽有(全开源)
  13. php导入rtf文件获取内容,可以使用PHP在网页中显示RTF文件吗?
  14. FreeCAD源码分析:Assembly4模块
  15. Python的运行加速:C究竟比python快在哪
  16. Enco free2 固件降级详解
  17. twitter跳过手机验证_twitter跳过手机验证_twitter手机验证不了_攻略
  18. 裁剪算法(计算机图形学)
  19. WordPress独立下载页面插件
  20. 如何去除matlab存图时的白边

热门文章

  1. 昨晚月圆,有人变成大猩猩吗?
  2. 英国24岁大猩猩成精了 像人一样直立行走
  3. 【CSDN竞赛第四期】参赛体验(第四期徽章什么时候发呀
  4. 数据湖+数据中台,金山云大数据平台竞争力如何?
  5. 如何使用POI导出excel表格,以及处理浏览器无法识别下载文件的问题
  6. (六)局部线性嵌入(LLE)及python实现
  7. easypoi导出excel不设置样式_解决EasyPoi导出excel文件后打开提示格式错误的问题
  8. 在html中使用fontIcon 的图标
  9. c++ 读取INI文件
  10. 记一道CTF中的phar反序列化