bzoj5084 hashit 广义SAM+树链的并
题目传送门
https://lydsy.com/JudgeOnline/problem.php?id=5084
题解
考虑平常对于静态问题,我们应该如何用 SAM 求本质不同的子串个数。
对于一个常规的 SAM,这个东西应该是 \(\sum\limits_{i\in V} len_i - len_{fa_i}\)。
很容易发现,我们如果把这个字符串每一个时刻的前一个字符和后一个字符给连接起来,这是一个树的关系。
考虑对这个树建立一棵广义 SAM。
但是上面的结论在广义 SAM 中不适用。不适用的是 \(i\) 的条件。
如果固定了当前的串是树上的哪一条链,这里就不应该是 \(i \in V\) 了,而是 \(i\) 代表的子串(等价类)在这个串中出现过。
这个东西显然就是对于这个串,每一个前缀所在的 \(endpos\) 集合的等价类的点在 parent 树上的链的并的长度了。
因为每一次的添加或删除字符影响的只有一个点,所以维护 \(parent\) 树上的树链的并来实现。
时间复杂度 \(O(n\log n)\) 。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 2e5 + 7;
int n, Q, nod, dfc;
ll ans = 0;
char s[N], v[N];
int id[N], fa[N], ip[N], dis[N];
int f[N], dep[N], siz[N], son[N], dfn[N], pre[N], top[N];
struct Edge { int to, ne, w; } g[N]; int head[N], tot;
inline void addedge(int x, int y, int z) { g[++tot].to = y, g[tot].w = z, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y, int z) { addedge(x, y, z), addedge(y, x, z); }
struct Node { int c[26], fa, len; } t[N];
inline int extend(int p, int x) {
if (t[p].c[x]) {
int q = t[p].c[x];
if (t[q].len == t[p].len + 1) return q;
int nq = ++nod;
t[nq] = t[q], t[nq].len = t[p].len + 1, t[q].fa = nq;
for (; t[p].c[x] == q; p = t[p].fa) t[p].c[x] = nq;
return nq;
}
int np = ++nod;
t[np].len = t[p].len + 1;
for (; p && !t[p].c[x]; p = t[p].fa) t[p].c[x] = np;
// dbg("p = %d, np = %d, nod = %d, x = %c\n", p, np, nod, x);
if (!p) t[np].fa = 1;
else {
int q = t[p].c[x];
if (t[q].len == t[p].len + 1) t[np].fa = q;
else {
int nq = ++nod;
t[nq] = t[q], t[nq].len = t[p].len + 1, t[q].fa = t[np].fa = nq;
for (; p && t[p].c[x] == q; p = t[p].fa) t[p].c[x] = nq;
}
}
// dbg("p = %d, np = %d, nod = %d, x = %c\n", p, np, nod, x);
assert(!t[1].fa);
return np;
}
inline void dfs1(int x, int fa = 0) {
f[x] = fa, dep[x] = dep[fa] + 1, siz[x] = 1;
for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
}
inline void dfs2(int x, int pa) {
top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
if (!son[x]) return; dfs2(son[x], pa);
for fec(i, x, y) if (y != f[x] && y != son[x]) dfs2(y, y);
}
inline int lca(int x, int y) {
while (top[x] != top[y]) dep[top[x]] > dep[top[y]] ? x = f[top[x]] : y = f[top[y]];
return dep[x] < dep[y] ? x : y;
}
inline void build() {
ip[1] = nod = 1;
for (int i = 2; i <= n; ++i)
ip[i] = extend(ip[fa[i]], v[i] - 'a');
for (int i = 2; i <= nod; ++i) addedge(t[i].fa, i, t[i].len - t[t[i].fa].len), dis[i] = t[i].len;
// for (int i = 1; i <= nod; ++i) dbg("i = %d, t[i].fa = %d, t[i].len = %d\n", i, t[i].fa, t[i].len);
}
struct cmp {
inline bool operator () (const int &x, const int &y) { return dfn[x] < dfn[y]; }
};
std::set<int, cmp> st;
inline void ins(int x) {
std::set<int, cmp>::iterator p = st.lower_bound(x);
int y = 0, z = 0;
ans += dis[x];
if (p != st.end()) y = *p, ans -= dis[lca(x, y)];
if (p != st.begin()) z = *--p, ans -= dis[lca(x, z)];
if (y && z) ans += dis[lca(y, z)];
st.insert(x);
}
inline void del(int x) {
st.erase(x);
std::set<int, cmp>::iterator p = st.lower_bound(x);
int y = 0, z = 0;
ans -= dis[x];
if (p != st.end()) y = *p, ans += dis[lca(x, y)];
if (p != st.begin()) z = *--p, ans += dis[lca(x, z)];
if (y && z) ans -= dis[lca(y, z)];
}
inline void work() {
build();
dfs1(1), dfs2(1, 1);
int now = 1;
for (int i = 1; i <= Q; ++i) {
if (s[i] == '-') del(ip[now]), now = fa[now];
else now = id[i], ins(ip[now]);
// dbg("now = %d, ip = %d\n", now, ip[now]);
printf("%lld\n", ans);
}
}
inline void init() {
scanf("%s", s + 1);
Q = strlen(s + 1);
int now = n = 1;
for (int i = 1; i <= Q; ++i) {
if (s[i] == '-') now = fa[now];
else fa[++n] = now, v[n] = s[i], now = n;
id[i] = now;
}
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
bzoj5084 hashit 广义SAM+树链的并的更多相关文章
- 51nod1600-Simple KMP【SAM,树链剖分】
正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1600 题目大意 给出一个字符串\(s\),每次在最后插入一个字符后求它的 ...
- 【bzoj5084】hashit 广义后缀自动机+树链的并+STL-set
题目描述 你有一个字符串S,一开始为空串,要求支持两种操作 在S后面加入字母C 删除S最后一个字母 问每次操作后S有多少个两两不同的连续子串 输入 一行一个字符串Q,表示对S的操作 如果第i个字母是小 ...
- 【bzoj5084】 hashit(广义SAM+set)
题面 传送门 题解 后缀平衡树是个啥啊我不会啊-- 那么我们来考虑一下\(SAM\)的做法好了 不难发现它的本义是要我们维护一棵\(trie\)树,并求出\(trie\)树上每一个节点到根的这段串的不 ...
- CF G. Indie Album 广义后缀自动机+树链剖分+线段树合并
这里给出一个后缀自动机的做法. 假设每次询问 $t$ 在所有 $s$ 中的出现次数,那么这是非常简单的: 直接对 $s$ 构建后缀自动机,随便维护一下 $endpos$ 大小就可以. 然而,想求 $t ...
- 关于广义后缀树(多串SAM)的总结
之前我们给的SAM的例题,基本上是一个串建SAM的就能做的 如果要建多个串的SAM应该怎么做呢 首先看题,bzoj2780 我一开始的想法是SA以前的弄法,把串拼起来,中间加分隔符做SAM 这题确实可 ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线
传送门 朴素想法:对\(M\)个匹配串\(T_1,...,T_M\)建立广义SAM,对于每一次询问,找到这个SAM上\(S[pl...pr]\)对应的状态,然后计算出对于每一个\(i \in [l,r ...
- CF666E-Forensic Examination【广义SAM,线段树合并】
正题 题目链接:https://www.luogu.com.cn/problem/CF666E 解题思路 给出一个串\(S\)和\(n\)个串\(T_i\).\(m\)次询问\(S_{a\sim b} ...
- CF204E-Little Elephant and Strings【广义SAM,线段树合并】
正题 题目链接:https://www.luogu.com.cn/problem/CF204E 题目大意 \(n\)个字符串的一个字符串集合,对于每个字符串求有多少个子串是这个字符串集合中至少\(k\ ...
随机推荐
- you have not created a boot efi partition
rhel6.8 自定义分区,创建 /boot/efi 分区找不到”EFI System Partition“文件系统, 选其他文件系统 next时都提示:you have not created a ...
- 插桩 inline hook 动态二进制插桩的原理和基本实现过程
插桩测试 https://source.android.google.cn/compatibility/tests/development/instrumentation https://zhuanl ...
- ASP.NET Core 上传微信永久视频素材
话不多说直接上源码 请求实体 public class AddVideoRequest { /// <summary> /// 文件流 / ...
- redis和memcached的对比与选型
相似处: 1:Memcached与Redis都属于内存内.键值数据存储方案.均属于NoSQL家族,而且都基于同样的键值数据模型.双方都选择将全部数据保存在内存当中,这自然也就让它们成为非常理想 ...
- 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_04 数据结构_2_数据结构_队列
先进先出 队列 队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入, 而在表的另一端进行删除. 简单的说,采用该结构的集合,对元素的存取有如下的特点: ...
- 应用安全-安全设备-Waf系列-软Waf-D盾
安装 下载http://www.d99net.net/down/d_safe_2.1.5.2.zip 使用说明 http://www.d99net.net/News.asp?id=106 免杀 arr ...
- 详细解析arry.map() ,function.apply() 方法
首先转载一篇博文:关于map 和callbackfn 的一些参数和返回值可以查看以下链接. http://www.cnblogs.com/xuan52rock/p/4460938.html array ...
- 【mysql连接与mysqldump】INFORMATION_SESSION_VARIABLES feature is disabled
摘要: mysql5.7 客户端连接出现:INFORMATION_SESSION_VARIABLES feature is disabled问题: mysqldump: Couldn't execut ...
- 【洛谷p3956】棋盘
日常blog(✧◡✧) 棋盘[题目链接] 算法: 然后这是2017普及组: first.关于颜色处理:让c[i][j]=color+1:这样无色=0,红色=1,黄色=2: 然后其实是记忆化,将记答案的 ...
- [2019杭电多校第十场][hdu6701]Make Rounddog Happy
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6701 题目大意为求满足 $max(a_{l},a_{l+1}\cdot \cdot \cdot a_{ ...