题目描述

(我并不想告诉你题目名字是什么鬼)

有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].

现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.

输入

第一行两个正整数n,m,分别表示S的长度以及询问的次数.

接下来一行有一个字符串S.

接下来有m组询问,对于每一组询问,均按照以下格式在一行内给出:

首先是一个整数t,表示共有多少个后缀.接下来t个整数分别表示t个后缀在字符串S中的出现位置.

输出

对于每一组询问,输出一行一个整数,表示该组询问的答案.由于答案可能很大,仅需要输出这个答案对于23333333333333333(一个巨大的质数)取模的余数.

样例输入

7 3
popoqqq
1 4
2 3 5
4 1 2 5 6

样例输出

0
0
2


题解

后缀数组+倍增RMQ+单调栈

首先预处理出sa和height数组。

然后对于每组询问,将要求的后缀去重后按照rank从小到大排序。

由于我们有:LCP(a,c)=min(LCP(a,b),LCP(b,c)),其中rank[a]<rank[b]<rank[c]

所以我们只需要知道相邻两个要求的后缀之间的LCP,即可推出任意两个后缀的LCP。

这里求LCP的方式是倍增RMQ,所以我偷改了height的定义:height[i][j]表示排名为i-2^j的后缀与排名为i的后缀的LCP。

这样转化成了一个新的问题:给你n个数,求其每个子区间中最小值的和。

考虑对答案的贡献:ai对答案的贡献是满足l∈[lpos,i],r∈[i,rpos]的所有区间[l,r],也即ai*(i-lpos+1)*(rpos-i+1),其中lpos是i左侧最后一个大于i的,rpos是i右侧最后一个大于等于i的。

(左右包含等号的情况不同是为了处理相同的数,防止重复或漏算)

可以用一个单调栈来在线性时间内求出i-lpos+1和rpos-i+1,具体方法见代码。

最后的最后,需要把用于去重的数组vis清零,注意不能用memset。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 500010
#define mod 23333333333333333ll
using namespace std;
int n , m , sa[N] , r[N] , ws[N] , wa[N] , wb[N] , wv[N] , rank[N] , height[N][21] , log[N] , num[N * 6] , vis[N] , pos[N] , val[N] , sta[N] , top , lp[N] , rp[N];
char str[N];
void da()
{
int i , j , p , *x = wa , *y = wb;
for(i = 0 ; i < m ; i ++ ) ws[i] = 0;
for(i = 0 ; i < n ; i ++ ) ws[x[i] = r[i]] ++ ;
for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];
for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[x[i]]] = i;
for(p = j = 1 ; p < n ; j <<= 1 , m = p)
{
for(p = 0 , i = n - j ; i < n ; i ++ ) y[p ++ ] = i;
for(i = 0 ; i < n ; i ++ ) if(sa[i] - j >= 0) y[p ++ ] = sa[i] - j;
for(i = 0 ; i < n ; i ++ ) wv[i] = x[y[i]];
for(i = 0 ; i < m ; i ++ ) ws[i] = 0;
for(i = 0 ; i < n ; i ++ ) ws[wv[i]] ++ ;
for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];
for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[wv[i]]] = y[i];
for(swap(x , y) , x[sa[0]] = 0 , p = i = 1 ; i < n ; i ++ )
{
if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) x[sa[i]] = p - 1;
else x[sa[i]] = p ++ ;
}
}
for(i = 1 ; i < n ; i ++ ) rank[sa[i]] = i;
for(p = i = 0 ; i < n - 1 ; height[rank[i ++ ]][0] = p)
for(p ? p -- : 0 , j = sa[rank[i] - 1] ; r[i + p] == r[j + p] ; p ++ );
}
int query(int x , int y)
{
x ++ ;
int k = log[y - x + 1];
return min(height[x + (1 << k) - 1][k] , height[y][k]);
}
bool cmp(int a , int b)
{
return rank[a] < rank[b];
}
int main()
{
int i , j , k , cnt , tot;
long long ans;
scanf("%d%d%s" , &n , &k , str);
for(i = 0 ; i < n ; i ++ ) r[i] = str[i] - 'a' + 1;
n ++ , m = 28 , da() , n -- ;
for(i = 2 ; i <= n ; i ++ ) log[i] = log[i >> 1] + 1;
for(i = 1 ; i <= log[n] ; i ++ )
for(j = (1 << i) ; j <= n ; j ++ )
height[j][i] = min(height[j][i - 1] , height[j - (1 << (i - 1))][i - 1]);
while(k -- )
{
scanf("%d" , &cnt);
tot = 0 , ans = 0;
for(i = 1 ; i <= cnt ; i ++ )
{
scanf("%d" , &num[i]) , num[i] -- ;
if(!vis[num[i]]) vis[num[i]] = 1 , pos[++tot] = num[i];
}
sort(pos + 1 , pos + tot + 1 , cmp);
for(i = 1 ; i < tot ; i ++ )
val[i] = query(rank[pos[i]] , rank[pos[i + 1]]);
sta[0] = top = 0;
for(i = 1 ; i < tot ; i ++ )
{
while(top && val[sta[top]] > val[i]) top -- ;
lp[i] = i - sta[top] , sta[++top] = i;
}
sta[0] = tot , top = 0;
for(i = tot - 1 ; i ; i -- )
{
while(top && val[sta[top]] >= val[i]) top -- ;
rp[i] = sta[top] - i , sta[++top] = i;
}
for(i = 1 ; i < tot ; i ++ ) ans = (ans + (long long)lp[i] * rp[i] * val[i]) % mod;
printf("%lld\n" , ans);
for(i = 1 ; i <= cnt ; i ++ ) vis[num[i]] = 0;
}
return 0;
}

【bzoj3879】SvT 后缀数组+倍增RMQ+单调栈的更多相关文章

  1. 【bzoj5073】[Lydsy1710月赛]小A的咒语 后缀数组+倍增RMQ+贪心+dp

    题目描述 给出 $A$ 串和 $B$ 串,从 $A$ 串中选出至多 $x$ 个互不重合的段,使得它们按照原顺序拼接后能够得到 $B$ 串.求是否可行.多组数据. $T\le 10$ ,$|A|,|B| ...

  2. BZOJ3879:SvT(后缀数组,单调栈,ST表)

    Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始 ...

  3. 【bzoj4516】[Sdoi2016]生成魔咒 后缀数组+倍增RMQ+STL-set

    题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔咒串 [1,2].一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒. 例如 S=[1,2 ...

  4. BZOJ 4453: cys就是要拿英魂![后缀数组 ST表 单调栈类似物]

    4453: cys就是要拿英魂! Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 90  Solved: 46[Submit][Status][Discu ...

  5. poj 3415 Common Substrings - 后缀数组 - 二分答案 - 单调栈

    题目传送门 传送点I 传送点II 题目大意 给定串$A, B$,求$A$和$B$长度大于等于$k$的公共子串的数量. 根据常用套路,用一个奇怪的字符把$A$,$B$连接起来,然后二分答案,然后按mid ...

  6. 洛谷P2178 品酒大会【后缀数组】【单调栈】

    题目描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainb ...

  7. bzoj千题计划313:bzoj3879: SvT(后缀数组+st表+单调栈)

    https://www.lydsy.com/JudgeOnline/problem.php?id=3879 把所有的后缀取出,按rank排序 求出相邻两个后缀的lcp 每个后缀对答案的贡献就是 与在它 ...

  8. bzoj千题计划314:bzoj3238: [Ahoi2013]差异(后缀数组+st表+单调栈)

    https://www.lydsy.com/JudgeOnline/problem.php?id=3238 跟 bzoj3879 差不多 #include<cstdio> #include ...

  9. bzoj3879 SvT(后缀自动机+虚树)

    bzoj3879 SvT(后缀自动机+虚树) bzoj 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置 ...

随机推荐

  1. httpd2.4.6三种工作模式(如何配置),防止占用内存暴增的策略

    之前偷懒默认用yum安装了httpd.后来发现服务器内存暴增,一度达到75% 打开一看,好嘛后台休眠进程全是httpd. 重启之后再度访问发现内存还是稳步增长. [root@iz2ze3ayxs2yp ...

  2. Android(java)学习笔记154:采用HttpClient提交数据(qq登录案例)

    1.Apache -Httpclient HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的.最新的.功能丰富的支持 HTTP 协议的客户端编程工具包 ...

  3. 2018.6.9 MyEclipse连接Oracle数据库方法及步骤

    在windows栏找到showXXX 然后选择最后一个others 找到Database 然后选择oracle 接着就是jar包的问题了 这个不同于mysql oracle如果装在了本机上面可以在文件 ...

  4. python_37_文件修改

    f=open('yesterday','r',encoding='utf-8') f_new=open('yesterday_update','w',encoding='utf-8') for lin ...

  5. DeepLearning tutorial(3)MLP多层感知机原理简介+代码详解

    本文介绍多层感知机算法,特别是详细解读其代码实现,基于python theano,代码来自:Multilayer Perceptron,如果你想详细了解多层感知机算法,可以参考:UFLDL教程,或者参 ...

  6. 启动Jmeter时遇到的几种错误

    1.权限不够 解决办法:用管理员权限运行 2.sdk版本太低 解决办法:1)查看当前sdk版本:java -version 2)安装sdk1.7或以上版本(jmeter3.0版本要用sdk1.7及以上 ...

  7. oo作业第四单元总结暨结课总结

    目录 一.第四单元作业架构设计 1.第一次UML作业架构设计 2.第二次UML作业架构设计 二.架构设计和OO方法理解演进 三.测试理解与实践的演进 四.课程收获总结 五.三个具体改进建议 一.第四单 ...

  8. Beyond Compare在Mac下永久试用

    转自 作者:忆如初 链接:https://www.jianshu.com/p/596b4463eacd 来源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处. 亲测可用 一. ...

  9. java面向对象思想1

    1.面向对象是面向过程而言.两者都是一种思想.面向过程:强调的是功能行为.(强调过程.动作)面向对象:将功能封装进对象,强调了具备了功能的对象.(强调对象.事物)面向对象是基于面向过程的.将复杂的事情 ...

  10. JS中的引用、浅拷贝和深拷贝

    js的深拷贝浅拷贝是很常遇到的问题,一直模模糊糊有点说不过去,所以这次好好总结一下. 1.js的引用 JS分为基础类型和引用类型两种数据类型: 基础类型:number.string.boolean.n ...