因为机房里的小伙伴都在看《你的名字。》而我不想看

所以来写了这道题...

给一个 $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 你的名字的更多相关文章

  1. [LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字

    [LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字 题意 给定一个大串 \(S\) 以及 \(q\) 次询问, 每次询问给定一个串 \(T\) 和区间 \([l, ...

  2. NOI 2018 你的名字 (后缀自动机+线段树合并)

    题目大意:略 令$ION2017=S,ION2018=T$ 对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$ 由于$T$必须在$[l,r ...

  3. NOI 2018 酱油记

    转眼离 NOI 2018 已经过了一个星期了,退役的我还是随便来水水吧. 语法.错字之类的可能会很多,但是我也不拘这点小节了. 恭喜 yww, zjt, sk 进队,zwl, myh au , yay ...

  4. [LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程

    [LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程 题意 给定一张无向图, 每条边有一个距离和一个高度. 再给定 \(q\) 组可能在线的询问, 每组询问给定一个点 ...

  5. [LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士

    [LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士 题意 题面好啰嗦啊直接粘LOJ题面好了 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照 ...

  6. NOI 2018网络同步赛(游记?)

    刚中考完那段时间比较无聊,报名了一个同步赛,报完名才发现成绩单是要挂到网上的,而且因为报的早给了一个很靠前的考号...那布星啊,赶紧学点东西,于是在一周内学了网络流,Treap以及一些数论. Day1 ...

  7. [NOI 2018] 归程

    Description 传送门 Solution 65分做法 先求出每个点到\(1\)号点的最短路,记为\(d[i]\).然后按照海拔从大到小依次加边,并查集维护每个连通块中\(d[i]\)的最小值, ...

  8. 解题:NOI 2018 归程

    题面 清新友好的题目 跑一个最短路,然后对海拔建Kruskal重构树,从最后接上去的边(最低的一个)开始DFS一下处理子树里路程的最小值. 询问是每次在重构树上倍增找到深度最浅的海拔高于当天水位线的节 ...

  9. 【NOI 2018】屠龙勇士(扩欧)

    题意理解错了... 一把剑打一条龙,打了$x$次后如果龙不死,你就Game Over了. 显然,面对每条龙使用的剑是固定的,如果所有龙中有一条没打死你就挂了. 可以知道,可行的答案集合就是所有龙的可行 ...

随机推荐

  1. NGUI如何使用汉字

    1:准备好字体文件,就是ttf后缀名的那些.. 2:在第一个红线部分,将下拉框选择为Unity,在后面的字体里面选择第一步准备好的字体. 3:创建UILabel,widget里面的Color才是字体的 ...

  2. 【HTML5校企公益课】第三天

        1.上午2D.旋转变色的... 基本思路就是先写静态画面然后添加动画. <!--告诉浏览器该文件为网页格式--> <html> <!--网页的头部标签--> ...

  3. Nginx URL跳转

    配置需求 注:$document_uri  表示访问的url 需求:访问 www.abc.com  请求到 www.abc.com/abc/ 使用操作 1.在nginx配置文件中加入 if ($doc ...

  4. 20145201《Java程序设计》第1次实验报告

    实验内容 一.命令行下java程序开发 1.建立Code目录,输入mkdir 20145201命令建立实验目录,并使用dir命令查看目录建立情况. 运行结果如图 2.进入实验目录,输入mkdir ex ...

  5. 一些好用的Linux命令工具

    刚好看到一个系列的博文,讲的就是那些平时很少被人用到但是很实用的命令,选一些觉得自己应该比较常用到的出来写个笔记. 这些命令有些不是系统命令,需要自行下载安装.Debian下安装:sudo apt-g ...

  6. Python 类的方法,下划线有什么不同?

    问: 1,以一个下划线开头的命名 ,如_getFile2,以两个下划线开头的命名 ,如__filename3,以两个下划线开头和结尾的命名,如 __init__()4,其它这些命名有什么不同吗? 答:

  7. codeforces 808D

    题意:给出一个序列,询问是否能移动一个数(或不操作)使得序列能分为左右两个和相等的子序列. 思路:对每个数处理最左边和最右边出现的位置.设置断点分左右区间,左右区间和差值的一半就是要找的数,进行判断. ...

  8. 分享会上演示Dubbo的路由规则,结果被小坑了下

      今天公司给大家演示基于Dubbox的服务治理平台,结果因为开了vpn导致本机IP地址比较混乱,在配置路由规则的时候我这样配置条件路由 consumer.host=*.*.*.* => pro ...

  9. HIVE 编写自定义函数UDF

    一 新建JAVA项目 并添加 hive-exec-2.1.0.jar 和hadoop-common-2.7.3.jar hive-exec-2.1.0.jar 在HIVE安装目录的lib目录下 had ...

  10. oracle 结构化语言查询 DML DDL DCL

    --结构化查询语言 (Structured Query Language),具有定义. --查询.更新和控制等多种功能,是关系数据库的标准语言. --SQL分类: -- 数据操纵语言DML Data ...