对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+主席树)的更多相关文章

  1. Luogu4770 NOI2018 你的名字 SAM、主席树

    传送门 UPD:发现之前被smy误导的一个细节,改过来之后就AC了-- 一道比较套路的SAM题,虽然我连套路都不会-- 先考虑前\(68pts\),也就是\(l=1 , r=|S|\)的情况.我们对\ ...

  2. [NOI2018]你的名字(SAM+线段树合并)

    考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献. T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长 ...

  3. NOI2018 你的名字——SAM+线段树合并

    题目链接在这里洛谷/LOJ 题目大意 有一个串\(S\),每次询问给你一个串\(T\),两个数\(L\)和\(R\),问你\(T\)有多少个本质不同的子串不是\(S[L,R]\)的子串 SOLUTIO ...

  4. 【BZOJ5417】[NOI2018]你的名字(线段树,后缀自动机)

    [BZOJ5417][NOI2018]你的名字(线段树,后缀自动机) 题面 BZOJ 洛谷 题解 首先考虑\(l=1,r=|S|\)的做法,对于每次询问的\(T\)串,暴力在\(S\)串的\(SAM\ ...

  5. 【NOI2019模拟2019.6.29】字符串(SA|SAM+主席树)

    Description: 1<=n<=5e4 题解: 考虑\(f\)这个东西应该是怎样算的? 不妨建出SA,然后按height从大到小启发式合并,显然只有相邻的才可能成为最优答案.这样的只 ...

  6. luogu4770 [NOI2018]你的名字 后缀自动机 + 线段树合并

    其实很水的一道题吧.... 题意是:每次给定一个串\(T\)以及\(l, r\),询问有多少个字符串\(s\)满足,\(s\)是\(T\)的子串,但不是\(S[l .. r]\)的子串 统计\(T\) ...

  7. Luogu4770 NOI2018你的名字(后缀自动机+线段树合并)

    先考虑l=1,r=n,并且不要求本质不同的情况.对原串建SAM,将询问串在上面跑,得到每个前缀的最长匹配后缀即可得到答案. 然后考虑本质不同.对询问串也建SAM,统计每个节点的贡献,得到该点right ...

  8. Luogu4770 NOI2018你的名字(后缀数组+线段树)

    即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现.即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇. 考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA.对 ...

  9. 【NOI2018】你的名字(SAM & 线段树合并)

    Description Hint Solution 不妨先讨论一下无区间限制的做法. 首先"子串"可以理解为"前缀的后缀",因此我们定义一个 \(\lim(i) ...

随机推荐

  1. 跨平台 webapp 开发技术之 Hybrid App

    前所知的 APP 开发模式有三种: 基于操作系统运行的 APP -> Native App,侧重于原生开发,用户体验好,需要安装才会升级 基于浏览器运行的 APP -> Web App,侧 ...

  2. MySQL 基础知识梳理学习(五)----详解MySQL两次写的设计及实现

    一 . 两次写提出的背景或要解决的问题 两次写(InnoDB Double Write)是Innodb中很独特的一个功能点.因为Innodb中的日志是逻辑的,所谓逻辑就是比如插入一条记录时,它可能会在 ...

  3. MongoDB 基本操作和聚合操作

    一 . MongoDB 基本操作 基本操作可以简单分为查询.插入.更新.删除. 1 文档查询 作用 MySQL SQL  MongoDB  所有记录  SELECT * FROM users;  db ...

  4. selenium-确认进入了预期页面(四)

    selenium确认进入了预期页面 在自动化操作中,浏览器每次进入一个新的需要,都需要确认该页面是否打开或打开的页面是否是预期的页面 需要进行确认页面后方可进行下一步操作 确认页面有很多中方法,像笔者 ...

  5. anaconda --包管理和环境管理器

    前言: 以下所有的命令都Win10环境的 Anaconda Prompt 中 环境管理 创建虚拟环境 conda create --name env_name python 也可以指定 Python ...

  6. SQL SELECT DISTINCT 语句

    SQL SELECT DISTINCT 语句 在表中,可能会包含重复值.这并不成问题,不过,有时您也许希望仅仅列出不同(distinct)的值. 关键词 DISTINCT 用于返回唯一不同的值. 语法 ...

  7. VS2017离线安装包[百度云盘](收藏了)

    *************************************************************************************************** ...

  8. 虚拟机配置Linux上网环境

    概要:在虚拟机安装CentOS6.5的环境后,配置NAT模式,修改系统文件支持上网. (1)ip地址的配置,IP地址的子网掩码为255.255.255.0. (2)网关的指定,也就是默认路由,当我们需 ...

  9. 腾讯大数据平台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 ...

  10. iowait 过高问题的查找及解决linux

    Linux 有许多可用来查找问题的简单工具,也有许多是更高级的 I/O Wait 就是一个需要使用高级的工具来debug的问题,当然也有许多基本工具的高级用法.I/O wait的问题难以定位的原因是因 ...