NOI 2018 你的名字
因为机房里的小伙伴都在看《你的名字。》而我不想看
所以来写了这道题...
给一个 $S$ 串,$q$ 次询问,每次一个 $T$ 串,问 $T$ 有多少没在 $S[l,r]$ 中以子串形式出现过的本质不同的子串
$|S|,q \leq 5e5,\sum |T| \leq 5e5$
sol:
容斥一下就变成了 $T$ 与 $S[l,r]$ 有多少本质不同的公共子串
首先把 $T$ 串在 $S$ 串上跑,跑不动就跳 $parent$,这样可以预处理出 $T$ 的每一个前缀能在 $S[l,r]$ 中匹配的最长后缀长度 $len_i$,具体就用可持久化线段树合并维护每个点的 $right$ 集合,然后每次维护一个 $now$,查这个点的 $right$ 集合里有没有 $[l+now-1,r]$ 即可,这样做的话发现跑的节点和这个 $now$ 好像构成了一个双指针的样子,所以复杂度是对的
然后在 $T$ 串的后缀自动机上按每个状态记录答案即可
#include <bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
int x = ,f = ; char ch = getchar();
for(; !isdigit(ch); ch = getchar())if(ch == '-') f = -f;
for(; isdigit(ch); ch = getchar())x = * x + ch - '';
return x * f;
}
const int maxn = 2e6 + ;
int n, q;
char s[maxn];
int root[maxn], ToT;
struct Node {
int val;
int ls, rs;
} st[maxn << ];
inline void Insert(int &x, int l, int r, int pos) {
if(!x) x = ++ToT; if(l == r) return (void) (st[x].val++);
int mid = (l + r) >> ;
(pos <= mid) ? Insert(st[x].ls, l, mid, pos) : Insert(st[x].rs, mid+, r, pos);
return (void) (st[x].val = st[st[x].ls].val + st[st[x].rs].val);
}
inline int query(int x, int l, int r, int L, int R) {
if(L <= l && r <= R) return st[x].val;
int mid = (l + r) >> , ans = ;
if(L <= mid) ans += query(st[x].ls, l, mid, L, R);
if(R > mid) ans += query(st[x].rs, mid+, r, L, R);
return ans;
}
inline int merge(int x, int y, int l, int r) {
if(!x || !y) return x + y;
int z = ++ToT;
if(l == r) st[z].val = st[x].val + st[y].val;
else {
int mid = (l + r) >> ;
st[z].ls = merge(st[x].ls, st[y].ls, l, mid);
st[z].rs = merge(st[x].rs, st[y].rs, mid+, r);
st[z].val = st[st[z].ls].val + st[st[z].rs].val;
}
return z;
}
vector<int> G[maxn]; int ans[maxn];
namespace SAM1 {
int first[maxn], to[maxn << ], nx[maxn << ], cnt;
void add(int u, int v) {
to[++cnt] = v;
nx[cnt] = first[u];
first[u] = cnt;
}
int rot, last, dfn;
int tr[maxn][], fa[maxn], mxlen[maxn];
void extend(int c, int id) {
int p = last, np = last = ++dfn;
mxlen[np] = mxlen[p] + ; Insert(root[np], , n, id);
for(; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
if(!p) fa[np] = rot;
else {
int q = tr[p][c];
if(mxlen[q] == mxlen[p] + ) fa[np] = q;
else {
int nq = ++dfn;
mxlen[nq] = mxlen[p] + ;
memcpy(tr[nq], tr[q], sizeof(tr[nq]));
fa[nq] = fa[q]; fa[q] = fa[np] = nq;
for(; p && tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
}
}
}
void build() {
rep(i, , dfn) {
add(fa[i], i);
//cout << i << " " << fa[i] << endl;
}
}
void dfs(int x) {
for(int i=first[x];i;i=nx[i]) dfs(to[i]), root[x] = merge(root[x], root[to[i]], , n);
}
void Query(char *s, int l, int r) {
int len = strlen(s + ); int p = rot, now = ;
for(int i = ; i <= len; i++){
int c = s[i] - 'a';
for(; p && !tr[p][c]; p = fa[p], now = mxlen[p]);
if(!p) { p = rot, now = ; continue; };
p = tr[p][c], now++;
while(p > ) {
if(query(root[p], , n, l + now - , r)) break;
now--;
if(now == mxlen[fa[p]]) p = fa[p];
}
if(p == rot) continue;
for(int j = ; j < (int) G[i].size(); j++) {
ans[G[i][j]] = max(ans[G[i][j]], now);
//cout << now << endl;
}
}
}
} // namespace SAM1
namespace SAM2 {
int rot, last, dfn;
int tr[maxn][], fa[maxn], mxlen[maxn];
void init() {
rep(i, , dfn) {
ans[i] = fa[i] = mxlen[i] = ;
memset(tr[i], , sizeof(tr[i]));
} rot = last = dfn = ;
}
void extend(int c, int id) {
int p = last, np = last = ++dfn;
mxlen[np] = mxlen[p] + ; G[id].push_back(np);
for(; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
if(!p) fa[np] = rot;
else {
int q = tr[p][c];
if(mxlen[q] == mxlen[p] + ) fa[np] = q;
else {
int nq = ++dfn;
G[id].push_back(nq);
mxlen[nq] = mxlen[p] + ;
fa[nq] = fa[q]; fa[q] = fa[np] = nq;
memcpy(tr[nq], tr[q], sizeof(tr[q]));
for(; p && tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
}
}
}
LL Query() {
LL ans1 = , ans2 = ;
rep(i, , dfn) {
if(ans[i] > mxlen[fa[i]]) ans2 += min(ans[i], mxlen[i]) - mxlen[fa[i]];
ans1 += mxlen[i] - mxlen[fa[i]];
// cout << ans[i] << endl;
//cout << ans1 << " " << ans2 << endl;
}
return ans1 - ans2;
}
} // namespace SAM2
int main() {
SAM1::rot = SAM1::last = ++SAM1::dfn;
scanf("%s", s + ); n = strlen(s + );
rep(i, , n) SAM1::extend(s[i] - 'a', i);
SAM1::build(); SAM1::dfs();
q = read();
while(q--) {
scanf("%s", s + ); int l = read(), r = read();
int len = strlen(s + );
rep(i, , len) G[i].clear();
SAM2::init();
rep(i, , len) SAM2::extend(s[i] - 'a', i);
SAM1::Query(s, l, r);
printf("%lld\n", SAM2::Query());
}
}
NOI 2018 你的名字的更多相关文章
- [LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字
[LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字 题意 给定一个大串 \(S\) 以及 \(q\) 次询问, 每次询问给定一个串 \(T\) 和区间 \([l, ...
- NOI 2018 你的名字 (后缀自动机+线段树合并)
题目大意:略 令$ION2017=S,ION2018=T$ 对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$ 由于$T$必须在$[l,r ...
- NOI 2018 酱油记
转眼离 NOI 2018 已经过了一个星期了,退役的我还是随便来水水吧. 语法.错字之类的可能会很多,但是我也不拘这点小节了. 恭喜 yww, zjt, sk 进队,zwl, myh au , yay ...
- [LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程
[LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程 题意 给定一张无向图, 每条边有一个距离和一个高度. 再给定 \(q\) 组可能在线的询问, 每组询问给定一个点 ...
- [LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士
[LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士 题意 题面好啰嗦啊直接粘LOJ题面好了 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照 ...
- NOI 2018网络同步赛(游记?)
刚中考完那段时间比较无聊,报名了一个同步赛,报完名才发现成绩单是要挂到网上的,而且因为报的早给了一个很靠前的考号...那布星啊,赶紧学点东西,于是在一周内学了网络流,Treap以及一些数论. Day1 ...
- [NOI 2018] 归程
Description 传送门 Solution 65分做法 先求出每个点到\(1\)号点的最短路,记为\(d[i]\).然后按照海拔从大到小依次加边,并查集维护每个连通块中\(d[i]\)的最小值, ...
- 解题:NOI 2018 归程
题面 清新友好的题目 跑一个最短路,然后对海拔建Kruskal重构树,从最后接上去的边(最低的一个)开始DFS一下处理子树里路程的最小值. 询问是每次在重构树上倍增找到深度最浅的海拔高于当天水位线的节 ...
- 【NOI 2018】屠龙勇士(扩欧)
题意理解错了... 一把剑打一条龙,打了$x$次后如果龙不死,你就Game Over了. 显然,面对每条龙使用的剑是固定的,如果所有龙中有一条没打死你就挂了. 可以知道,可行的答案集合就是所有龙的可行 ...
随机推荐
- boot空间不足,删除Ubuntu旧内核
0 Problem 今天打开电脑的时候ubuntu提示boot空间不足.查了资料,原来Ubuntu的自动升级并没有删除系统的旧内核,于是boot下旧的内核文件越积越多,最后就满了. 1 Solutio ...
- git-bash使用ctrl C无法终止nodemon的执行
原因: git的bug 解决:git版本降级为2.10.0好了
- Linux下代理服务器(proxy)配置
Linux下有很多程序都只有命令行接口,对于这类程序,它们通过代理服务器(proxy)访问网络的方式也不尽相同.在本文中Easwy总结了一些常用Linux程序配置代理服务器的方法. [ 通用代理服务器 ...
- Stalstack 连接管理配置
Stalstack 连接管理配置 注:master端,minion端,配置完成. Saltstack master 测试管理端minion链接状态. salt-key Accepted Keys: ...
- ElasticSearch高可用集群环境搭建和分片原理
1.ES是如何实现分布式高并发全文检索 2.简单介绍ES分片Shards分片技术 3.为什么ES主分片对应的备分片不在同一台节点存放 4.索引的主分片定义好后为什么不能做修改 5.ES如何实现高可用容 ...
- struts2——上传图片格式
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...
- Mysql 分组聚合实现 over partition by 功能
mysql中没有类似oracle和postgreSQL的 OVER(PARTITION BY)功能. 那么如何在MYSQL中搞定分组聚合的查询呢 先说结论: 利用 group_concat + sub ...
- quartz多任务调度+spring 实现
一.Quartz的学习简述 客官,不要急,请看完下面的内容... 代码可以直接拷贝使用,本文是编写2个定时方法来实现的,如果想要执行1个,删除另1个即可.但是想要知道执行原理请看最后的原理分析 二.执 ...
- Author Agreement
Dear Editor,We the undersigned declare that this manuscript entitled “文章标题” is original, has not bee ...
- angular-ui-bootstrap各版本下载地址
http://www.bootcdn.cn/angular-ui-bootstrap/