luogu4770 [NOI2018]你的名字 (SAM+主席树)
对S建SAM,拿着T在上面跑
跑的时候不仅无法转移要跳parent,转移过去不在范围内也要跳parent(注意因为范围和长度有关,跳的时候应该把长度一点一点地缩)
这样就能得到对于T的每个前缀,它最长的不合法的后缀的长度ill[i]
得到他要去重,以后可以再对T建SAM,然后对于每个节点,$ans+=max(0,len[i]-max(len[fa[i]],ill[pos[i]]))$,其中pos[i]是它的right集合中随便一个位置(因为每个位置的小于len的ill都一样)
那么怎么判在不在范围内呢..似乎可以线段树合并,带个log地求出每个节点的right
当然也可以直接dfs序然后建主席树
#include<bits/stdc++.h>
#define pa pair<int,int>
#define CLR(a,x) memset(a,x,sizeof(a))
#define MP make_pair
using namespace std;
typedef long long ll;
const int maxn=1e6+; inline char gc(){
return getchar();
static const int maxs=<<;static char buf[maxs],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,,maxs,stdin),p1==p2)?EOF:*p1++;
}
inline ll rd(){
ll x=;char c=gc();bool neg=;
while(c<''||c>''){if(c=='-') neg=;c=gc();}
while(c>=''&&c<='') x=(x<<)+(x<<)+c-'',c=gc();
return neg?(~x+):x;
} struct SAM{
int len[maxn*],fa[maxn*],tr[maxn*][],pct,lst,pos[maxn*]; inline void clear(){
while(pct){
CLR(tr[pct],);
len[pct]=fa[pct]=pos[pct]=;
pct--;
}pct=lst=;
} inline void insert(int x,bool b){
int p=++pct;
len[p]=len[lst]+;pos[p]=len[p];
int o=lst;lst=p;
for(;o&&!tr[o][x];o=fa[o]) tr[o][x]=p;
if(!o){fa[p]=;return;}
int q=tr[o][x];
if(len[q]==len[o]+){fa[p]=q;return;}
int qq=++pct;if(b) pos[qq]=pos[p];
len[qq]=len[o]+;fa[qq]=fa[q];
memcpy(tr[qq],tr[q],sizeof(tr[q]));
fa[q]=fa[p]=qq;
for(;o&&tr[o][x]==q;o=fa[o]) tr[o][x]=qq;
}
}S,T; char s[maxn];
int N,M,Q;
int ill[maxn];
int cnt[maxn],rnk[maxn],dfn[maxn][],id[maxn],tot;
vector<int> eg[maxn]; inline void dfs(int x){
id[++tot]=x;dfn[x][]=tot;
for(int i=;i<eg[x].size();i++) dfs(eg[x][i]);
dfn[x][]=tot;
} int rt[maxn],num[maxn*],ch[maxn*][],pct; inline void insert(int pre,int &p,int l,int r,int x,int y){
p=++pct;num[p]=num[pre]+y;
if(l<r){
int m=l+r>>;
if(x<=m) insert(ch[pre][],ch[p][],l,m,x,y),ch[p][]=ch[pre][];
else insert(ch[pre][],ch[p][],m+,r,x,y),ch[p][]=ch[pre][];
}
} inline int query(int pre,int p,int l,int r,int x,int y){
if(x>y) return ;
if(x<=l&&r<=y) return num[p]-num[pre];
int m=l+r>>,re=;
if(x<=m) re=query(ch[pre][],ch[p][],l,m,x,y);
if(y>=m+) re+=query(ch[pre][],ch[p][],m+,r,x,y);
return re;
} int main(){
//freopen("","r",stdin);
int i,j,k;
scanf("%s",s+);N=strlen(s+);
S.clear();
for(i=;i<=N;i++) S.insert(s[i]-'a',); for(i=;i<=S.pct;i++) eg[S.fa[i]].push_back(i);
dfs();
for(i=;i<=tot;i++){
if(S.pos[id[i]]) insert(rt[i-],rt[i],,N,S.pos[id[i]],);
else rt[i]=rt[i-];
} Q=rd();
for(i=;i<=Q;i++){
scanf("%s",s+);M=strlen(s+);
int l=rd(),r=rd();
int now=,nl=;
for(j=;j<=M;j++){
int x=s[j]-'a';
while(now&&!(S.tr[now][x]&&query(rt[dfn[S.tr[now][x]][]-],rt[dfn[S.tr[now][x]][]],,N,l+nl,r))){
if(!nl){now=;break;}
nl--;
if(nl==S.len[S.fa[now]]) now=S.fa[now];
}
if(now) nl++,now=S.tr[now][x];
else now=;
ill[j]=nl;
// printf("~%d %d\n",j,ill[j]);
}
T.clear();
for(j=;j<=M;j++) T.insert(s[j]-'a',);
ll ans=;
for(j=;j<=T.pct;j++){
ans+=max(,T.len[j]-max(T.len[T.fa[j]],ill[T.pos[j]]));
}
printf("%lld\n",ans);
}
return ;
}
luogu4770 [NOI2018]你的名字 (SAM+主席树)的更多相关文章
- Luogu4770 NOI2018 你的名字 SAM、主席树
传送门 UPD:发现之前被smy误导的一个细节,改过来之后就AC了-- 一道比较套路的SAM题,虽然我连套路都不会-- 先考虑前\(68pts\),也就是\(l=1 , r=|S|\)的情况.我们对\ ...
- [NOI2018]你的名字(SAM+线段树合并)
考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献. T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长 ...
- NOI2018 你的名字——SAM+线段树合并
题目链接在这里洛谷/LOJ 题目大意 有一个串\(S\),每次询问给你一个串\(T\),两个数\(L\)和\(R\),问你\(T\)有多少个本质不同的子串不是\(S[L,R]\)的子串 SOLUTIO ...
- 【BZOJ5417】[NOI2018]你的名字(线段树,后缀自动机)
[BZOJ5417][NOI2018]你的名字(线段树,后缀自动机) 题面 BZOJ 洛谷 题解 首先考虑\(l=1,r=|S|\)的做法,对于每次询问的\(T\)串,暴力在\(S\)串的\(SAM\ ...
- 【NOI2019模拟2019.6.29】字符串(SA|SAM+主席树)
Description: 1<=n<=5e4 题解: 考虑\(f\)这个东西应该是怎样算的? 不妨建出SA,然后按height从大到小启发式合并,显然只有相邻的才可能成为最优答案.这样的只 ...
- luogu4770 [NOI2018]你的名字 后缀自动机 + 线段树合并
其实很水的一道题吧.... 题意是:每次给定一个串\(T\)以及\(l, r\),询问有多少个字符串\(s\)满足,\(s\)是\(T\)的子串,但不是\(S[l .. r]\)的子串 统计\(T\) ...
- Luogu4770 NOI2018你的名字(后缀自动机+线段树合并)
先考虑l=1,r=n,并且不要求本质不同的情况.对原串建SAM,将询问串在上面跑,得到每个前缀的最长匹配后缀即可得到答案. 然后考虑本质不同.对询问串也建SAM,统计每个节点的贡献,得到该点right ...
- Luogu4770 NOI2018你的名字(后缀数组+线段树)
即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现.即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇. 考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA.对 ...
- 【NOI2018】你的名字(SAM & 线段树合并)
Description Hint Solution 不妨先讨论一下无区间限制的做法. 首先"子串"可以理解为"前缀的后缀",因此我们定义一个 \(\lim(i) ...
随机推荐
- 跨平台 webapp 开发技术之 Hybrid App
前所知的 APP 开发模式有三种: 基于操作系统运行的 APP -> Native App,侧重于原生开发,用户体验好,需要安装才会升级 基于浏览器运行的 APP -> Web App,侧 ...
- MySQL 基础知识梳理学习(五)----详解MySQL两次写的设计及实现
一 . 两次写提出的背景或要解决的问题 两次写(InnoDB Double Write)是Innodb中很独特的一个功能点.因为Innodb中的日志是逻辑的,所谓逻辑就是比如插入一条记录时,它可能会在 ...
- MongoDB 基本操作和聚合操作
一 . MongoDB 基本操作 基本操作可以简单分为查询.插入.更新.删除. 1 文档查询 作用 MySQL SQL MongoDB 所有记录 SELECT * FROM users; db ...
- selenium-确认进入了预期页面(四)
selenium确认进入了预期页面 在自动化操作中,浏览器每次进入一个新的需要,都需要确认该页面是否打开或打开的页面是否是预期的页面 需要进行确认页面后方可进行下一步操作 确认页面有很多中方法,像笔者 ...
- anaconda --包管理和环境管理器
前言: 以下所有的命令都Win10环境的 Anaconda Prompt 中 环境管理 创建虚拟环境 conda create --name env_name python 也可以指定 Python ...
- SQL SELECT DISTINCT 语句
SQL SELECT DISTINCT 语句 在表中,可能会包含重复值.这并不成问题,不过,有时您也许希望仅仅列出不同(distinct)的值. 关键词 DISTINCT 用于返回唯一不同的值. 语法 ...
- VS2017离线安装包[百度云盘](收藏了)
*************************************************************************************************** ...
- 虚拟机配置Linux上网环境
概要:在虚拟机安装CentOS6.5的环境后,配置NAT模式,修改系统文件支持上网. (1)ip地址的配置,IP地址的子网掩码为255.255.255.0. (2)网关的指定,也就是默认路由,当我们需 ...
- 腾讯大数据平台Oceanus: A one-stop platform for real time stream processing powered by Apache Flink
January 25, 2019Use Cases, Apache Flink The Big Data Team at Tencent In recent years, the increa ...
- iowait 过高问题的查找及解决linux
Linux 有许多可用来查找问题的简单工具,也有许多是更高级的 I/O Wait 就是一个需要使用高级的工具来debug的问题,当然也有许多基本工具的高级用法.I/O wait的问题难以定位的原因是因 ...