即求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. C++使用默认参数的构造函数

    我们可以想象一个这样的场景:某一天书店整理库存,发现了一些非常老的书,为了尽快清空库存,店主想了一下,决定开展一个大甩卖活动,所有的这些书全部以五美元的价格出售.此时如果需要尽快将这些书的信息录入到书 ...

  2. (转)postfix疯狂外发垃圾邮件之分析与解决

    从进程中看到,好像是postfix有问题.我这postfix主要是用来给程序发达邮件用的,如报警,程序外发邮件等.平时postfix进程不会像现在这样异常,这在postf主进程CPU占用高,其它的相关 ...

  3. 支持向量机通俗导论(理解SVM的三层境界)【非原创】

    支持向量机通俗导论(理解SVM的三层境界) 作者:July :致谢:pluskid.白石.JerryLead. 出处:结构之法算法之道blog. 前言 动笔写这个支持向量机(support vecto ...

  4. jquery的$(document).ready()与js的window.onload区别

    <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...

  5. 2.1《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)——重定向文件和添加文件

    回忆第一章节的内容,我们用echo命令输出莎士比亚的第一首十四行诗的第一行(Listing 6): $ echo "From fairest creatures we desire incr ...

  6. bundle install 安装的 gem 提示 cannot load such file

    /usr/local/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load ...

  7. c# HttpWebRequest Cookie 设置到 webBrowser 控件

    [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)] public static exte ...

  8. vue中v-if 和 v-show的区别

    简单来说,v-if 的初始化较快,但切换代价高:v-show 初始化慢,但切换成本低 1.共同点 v-if 和 v-show 都可以动态地显示DOM元素 2.区别 (1)手段: v-if 是动态的向D ...

  9. [LOJ#6044]. 「雅礼集训 2017 Day8」共[二分图、prufer序列]

    题意 题目链接 分析 钦定 \(k\) 个点作为深度为奇数的点,有 \(\binom{n-1}{k-1}\) 种方案. 将树黑白染色,这张完全二分图的生成树的个数就是我们钦定 \(k\) 个点之后合法 ...

  10. Paxos算法浅析

    前言在文章2PC/3PC到底是啥中介绍了2PC这种一致性协议,从文中了解到2PC更多的被用在了状态一致性上(分布式事务),在数据一致性中很少被使用:而Paxos正是在数据一致性中被广泛使用,在过去十年 ...