即求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. Android关于LinearLayout和RelativeLayout背景设置的区别

    1.LinearLayout:设置背景时即设置android:background时,假如LayoutLayout设置了android:layout_width="wrap_content& ...

  2. 开启路由转发 - route add -net 0.0.0.0 netmask 0.0.0.0 gateway 192.168.0.131 window tracert 追踪路由

    1.登录方式内网访问172.28.101.0/19网段的方法:在192.168.1.0/24网段的上网机器上,或在自己的操作机上加个192.168.1.0网段的ip,注意不要跟别人设置的冲突了,并添加 ...

  3. jqgrid 设置冻结列

    有时,jqgrid表格的列非常多,而表格的宽度值是固定的,我们需要在表格底部出现滚动条,并且固定前面几个列作为数据参照项,如何实现? 需要用的jqgrid冻结列,步骤如下: 1)设置需要冻结的列属性, ...

  4. Feeling_2018_5_22

    “我打你,你会走吗?”    “不会!!” “我骂你,你会走吗?”    “不会!!” “那我不爱你了,你会走吗?”       “会.”

  5. TreeSet排序相关总结

            java的集合这一块在工作中用得还比较多,有些东西老是忘,因此在此记录下来. TreeSet原理 1.特点 TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺 ...

  6. Sagas模式

    Sagas属于一个错误管理模式,也同时用于控制复杂事务的执行和回滚等.同时,Compensating-Transaction模式的的实现也是也是类似于Sagas策略的,可以对比参考一下. Sagas的 ...

  7. EZ 2018 02 26 NOIP2018 模拟赛(一)

    这次是校内OJ(HHHOJ)线上比赛,网址:http://211.140.156.254:2333/contest/51 (我去刚刚快写完了手贱关掉了) 这次总体难度也不高,T1&&T ...

  8. SQL面试整理(1)——数据库连接池

    在web开发中,如果JSP.Servlet或EJB使用JDBC直接访问数据库,每一次数据访问请求都必须经历建立数据库连接,打开数据库,存取数据库和关闭数据库连接等操作步骤,如果频繁发生这种数据库操作, ...

  9. Codeforces 954C Matrix Walk (思维)

    题目链接:Matrix Walk 题意:设有一个N×M的矩阵,矩阵每个格子都有从1-n×m的一个特定的数,具体数的排列如图所示.假设一个人每次只能在这个矩阵上的四个方向移动一格(上下左右),给出一条移 ...

  10. centos7 安装 telnet

    https://blog.csdn.net/wfh6732/article/details/55062016/ https://blog.csdn.net/typa01_kk/article/deta ...