即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现。即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇。

  考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA。对于每个询问,我们对串的每个后缀,求出其在给定区间中最长的lcp是多少。这样就能得到不考虑本质不同时的答案,再考虑该串名次数组中相邻后缀的lcp去一下重即可。

  考虑怎么求这个最长的lcp。设该后缀在名次数组中的位置是i,给定区间是[l,r],这个东西实际上就是max{min{lcp(i..j),r-saj+1}} (saj>=l)。容易发现取min的两个函数实际上一个单减一个单增,于是容易想到的做法是二分答案,找到名次数组上对应区间,查询区间内是否有合法的sa值。但这样就是log^2了。实际上按sa倒序建可持久化线段树(或者仍然离线就只需要线段树了),线段树上二分查询即可。这个二分需要自底向上再自顶向下以保证复杂度,写起来感觉非常恶心。

  非常神奇的是这份代码在uoj过掉了,在luogu被卡常,在lojT成0分。

  upd:更神奇的是深夜在luogu交了一次就过掉了,而本来只有64。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 1600010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
char s[N];
int len,n,m,a[N],sa[N],sa2[N],rk[N<<],tmp[N<<],cnt[N],h[N],f[][N],lg2[N],lcp[N],id[N];
ll ans[N];
struct data
{
int s,t,l,r,i;
bool operator <(const data&a) const
{
return l>a.l;
}
}q[N];
struct data2{int l,r,x;
}tree[N<<];
void make(int m)
{
for (int i=;i<=n;i++) cnt[rk[i]=a[i]]++;
for (int i=;i<=m;i++) cnt[i]+=cnt[i-];
for (int i=n;i>=;i--) sa[cnt[rk[i]]--]=i;
for (int k=;k<=n;k<<=)
{
int p=;
for (int i=n-k+;i<=n;i++) sa2[++p]=i;
for (int i=;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;
memset(cnt,,m+<<);
for (int i=;i<=n;i++) cnt[rk[i]]++;
for (int i=;i<=m;i++) cnt[i]+=cnt[i-];
for (int i=n;i>=;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];
memcpy(tmp,rk,sizeof(tmp));
p=;rk[sa[]]=;
for (int i=;i<=n;i++)
{
if (tmp[sa[i-]]!=tmp[sa[i]]||tmp[sa[i-]+k]!=tmp[sa[i]+k]) p++;
rk[sa[i]]=p;
}
m=p;if (m==n) break;
}
for (int i=;i<=n;i++)
{
h[i]=max(h[i-]-,);
while (a[i+h[i]]==a[sa[rk[i]-]+h[i]]) h[i]++;
}
for (int i=;i<=n;i++) f[][i]=h[sa[i]];
for (int i=;i<=n;i++)
{
lg2[i]=lg2[i-];
if ((<<lg2[i])<=i) lg2[i]++;
}
for (int j=;j<;j++)
for (int i=;i<=n;i++)
f[j][i]=min(f[j-][i],f[j-][min(n,i+(<<j-))]);
}
int query(int x,int y)
{
if (x==y) return N;x++;
return min(f[lg2[y-x+]][x],f[lg2[y-x+]][y-(<<lg2[y-x+])+]);
}
void build(int k,int l,int r)
{
tree[k].l=l,tree[k].r=r;tree[k].x=N;
if (l==r) {id[l]=k;return;}
int mid=l+r>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
}
int findl(int x,int r)
{
int ans=,k=id[x],c=tree[k].x;
while (k!=)
if (!(k&)) k>>=;
else if (r-min(c,tree[k-].x)+>=query(tree[k-].l,x)) {k>>=;break;}
else c=min(c,tree[k-].x),k>>=,ans=max(ans,min(query(tree[k].l,x),r-c+));
while (tree[k].l<tree[k].r&&tree[k].r>=x) k<<=;
while (tree[k].l<tree[k].r)
if (r-min(c,tree[k<<|].x)+>=query(tree[k<<|].l,x)) k=k<<|;
else k<<=,c=min(c,tree[k+].x),ans=max(ans,min(query(tree[k+].l,x),r-c+));
ans=max(ans,min(query(tree[k].l,x),r-min(tree[k].x,c)+));
return ans;
}
int findr(int x,int r)
{
int ans=,k=id[x],c=tree[k].x;
while (k!=)
if (k&) k>>=;
else if (r-min(c,tree[k+].x)+>=query(x,tree[k+].r)) {k>>=;break;}
else c=min(c,tree[k+].x),k>>=,ans=max(ans,min(query(x,tree[k].r),r-c+));
while (tree[k].l<tree[k].r&&tree[k].l<=x) k=k<<|;
while (tree[k].l<tree[k].r)
if (r-min(c,tree[k<<].x)+>=query(x,tree[k<<].r)) k<<=;
else k=k<<|,c=min(c,tree[k-].x),ans=max(ans,min(query(x,tree[k-].r),r-c+));
ans=max(ans,min(query(x,tree[k].r),r-min(tree[k].x,c)+));
return ans;
}
bool cmp(const int&a,const int&b)
{
return rk[a]<rk[b];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("name.in","r",stdin);
freopen("name.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
scanf("%s",s+);len=n=strlen(s+);
for (int i=;i<=n;i++) a[i]=s[i]-'a'+;
a[++n]=;
m=read();
for (int i=;i<=m;i++)
{
scanf("%s",s+);int t=strlen(s+);
q[i].s=n+;
for (int j=;j<=t;j++) a[++n]=s[j]-'a'+;
q[i].t=n;a[++n]=;
q[i].l=read(),q[i].r=read(),q[i].i=i;
}
sort(q+,q+m+);
make();
build(,,n);
q[].l=len+;
for (int i=;i<=m;i++)
{
for (int j=q[i-].l-;j>=q[i].l;j--)
for (int k=id[rk[j]];k;k>>=) tree[k].x=j;
for (int j=q[i].s;j<=q[i].t;j++) tmp[j]=j;
sort(tmp+q[i].s,tmp+q[i].t+,cmp);
for (int j=q[i].s;j<=q[i].t;j++) lcp[j]=max(findl(rk[tmp[j]],q[i].r),findr(rk[tmp[j]],q[i].r));
ans[q[i].i]=q[i].t-tmp[q[i].s]+-lcp[q[i].s];
for (int j=q[i].s+;j<=q[i].t;j++)
ans[q[i].i]+=q[i].t-tmp[j]+-max(lcp[j],query(min(rk[tmp[j]],rk[tmp[j-]]),max(rk[tmp[j]],rk[tmp[j-]])));
}
for (int i=;i<=m;i++) printf(LL,ans[i]);
return ;
}

Luogu4770 NOI2018你的名字(后缀数组+线段树)的更多相关文章

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

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

  2. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  3. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

  4. [NOI2018]你的名字(后缀自动机+线段树)

    题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...

  5. BZOJ.5417.[NOI2018]你的名字(后缀自动机 线段树合并)

    LOJ 洛谷 BZOJ 考虑\(l=1,r=|S|\)的情况: 对\(S\)串建SAM,\(T\)在上面匹配,可以得到每个位置\(i\)的后缀的最长匹配长度\(mx[i]\). 因为要去重,对\(T\ ...

  6. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  7. 【XSY1551】往事 广义后缀数组 线段树合并

    题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...

  8. BZOJ 2865 字符串识别 | 后缀数组 线段树

    集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...

  9. bzoj 1396: 识别子串 && bzoj 2865: 字符串识别【后缀数组+线段树】

    根据height数组的定义,和当前后缀串i最长的相同串的长度就是max(height[i],height[i+1]),这个后缀贡献的最短不同串长度就是len=max(height[i],height[ ...

随机推荐

  1. Arduino入门笔记(3):单LED闪烁

    转载请注明:@小五义http://www.cnblogs.com/xiaowuyi 欢迎加入讨论群 64770604 在搭建好arduino开发环境(http://www.cnblogs.com/xi ...

  2. day45

    今日内容 1.css三种引入方式 2.三种引入方式的优先级 3.长度及颜色单位 4.常用样式 5.css选择器 CSS三种引入方式 1.1css引入方式之行间式 ​ 行间式(特点): ​ 1.标签头部 ...

  3. rpm yum apt-get redhat centos ubuntu

    rpm是由红帽公司开发的软件包管理方式,使用rpm我们可以方便的进行软件的安装.查询.卸载.升级等工作.但是rpm软件包之间的依赖性问题往往会很繁琐,尤其是软件由多个rpm包组成时.Yum(全称为 Y ...

  4. odoo方法

    一. 类似于前面有个_ 的方法,格式是如下def _getdlvqty(self, cr, uid, ids, field_names, args, context=None): def _get_p ...

  5. Redis Replication

    Replication 官网说明:http://www.redis.io/topics/replication Redis使用异步复制; 一个Master可以有多个Slaves; Slaves可以接收 ...

  6. 同步IO和异步IO的区别

    首先一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作,同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO,因此阻塞IO.非阻塞IO.IO服用 ...

  7. 翻译 | The Principles of OOD 面向对象设计原则

    本文首发于vivo互联网技术微信公众号 https://mp.weixin.qq.com/s/Q_pziBUhKRywafKeY2T7YQ 作者:Robert C. Martin 翻译:张硕 本文由来 ...

  8. coco2d-x游戏逻辑结构

    在Cocos2d-x中开发游戏的主要逻辑和结构是:先创建场景,在场景上添加一层或多层,然后可以在指定层上添加精灵.菜单.文字等,可以为精灵.文字执行某个动作(或者移动),检测玩家触屏事件,开启任务调度 ...

  9. vue初学实践之路——vue简单日历组件(2)

    上一篇我们已经实现了基本的日历显示功能,这一次我们要加上预定的功能 废话不多说,上代码 <div id="calendar"> <!-- 年份 月份 --> ...

  10. FME Cloud 账号申请流程

    第一步,访问SAFE的FME Cloud注册页,官网明确表态,如果你是一个新的FME Cloud用户,你可以免费获得一个初级版.地址:https://console.fmecloud.safe.com ...