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了. 显然,面对每条龙使用的剑是固定的,如果所有龙中有一条没打死你就挂了. 可以知道,可行的答案集合就是所有龙的可行 ...
随机推荐
- 使用Linq to XML 修改app.config
使用其他的方法修改app.config无效.而且修改的是*.vshost.exe.Config,程序运行时正常,关闭之后就还是原来的值. Configuration configuration = C ...
- MySQL Binlog解析(2)
一.TABLE_MAP_EVENT Used for row-based binary logging beginning with MySQL 5.1.5.The TABLE_MAP_EVENT d ...
- 拓扑排序(附LeetCode题目)
算法期中考到一题关于拓扑序的题目,觉得很值得一写. 1.什么是拓扑序? 对一个有向无环图进行拓扑排序,假如图中存在一条从顶点A到顶点B的路径,则拓扑序中顶点A出现在顶点B的前面.要注意的是,这是对有向 ...
- PHP memcache扩展模块安装
安装php扩展模块memcache memcache 的工作就是在专门的机器的内存里维护一张巨大的hash表,来存储经常被读写的一些数组与文件,从而极大的提高网站的运行效率,减轻后端数据库的读写压力. ...
- 开机自动mount
root权限编辑:/etc/fstab vim /etc/fstab #当前系统里的唯一标志 挂载到什么地方 文件系统类型 选项 是否dump # <fi ...
- RN项目中缩进处理
SpannableString使用详解http://blog.csdn.net/u012702547/article/details/49895157 Spannable的用法http://www.j ...
- 在Linux系统下使用Github的基本教程
1. 安装git: sudo apt-get install git-core git-gui git-doc 2.到https://github.com/ 注册一个帐号,一会儿客户端登录的时候要使用 ...
- JDBCTemplate执行增删改查(CDUR)操作
1.JdbcTemplate简介 (1)Spring提供的一个操作数据库的技术JdbcTemplate,是对Jdbc的封装.语法风格非常接近DBUtil. JdbcTemplate可以直接操作数据库, ...
- springBean参数注入的几个方法
1.普通方式注入 applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> < ...
- Android高手进阶教程(十七)之---Android中Intent传递对象的两种方法(Serializable,Parcelable)!
[转][原文] 大家好,好久不见,今天要给大家讲一下Android中Intent中如何传递对象,就我目前所知道的有两种方法,一种是Bundle.putSerializable(Key,Object); ...