题目分析:

这道题我是乱搞的,因为他说$01$串是随机的。

那么我们可以猜测能够让LCP变大的地方很少。求出后缀数组之后可能让LCP变大的地方就等价于从大到小往height里动态加点同时维护这个点左右两边的单调栈。

这个事情用线段树模拟就行了。

用暴力跑一下发现果然不大,只有200w,其中很多还是废点,把废点特判一下删除掉就行了。

然后把询问离线,按r排序,再作扫描线,每次扫到r的时候把前面变动的LCP更新一下。然后对于询问也做一个类似上面单调栈的操作。

代码:

 #include<bits/stdc++.h>
#define RI register
using namespace std; const int maxn = ;
const int N = ; int n,q;
char str[maxn];
int T[maxn<<],a[maxn];
int RMQ[maxn][],pw[maxn]; struct node{int l,r,nb;}qy[maxn];
int ans[maxn];
int sa[maxn],rk[maxn],X[maxn],Y[maxn];
int ht[maxn],h[maxn]; void fstread(int &x){
char ch = getchar();x = ;
while(ch > '' || ch < '') ch = getchar();
while(ch >= '' && ch <= '') x = x*+ch-'',ch=getchar();
} int chk(int x,int k){
return rk[sa[x]]==rk[sa[x-]]&&rk[sa[x]+(<<k)]==rk[sa[x-]+(<<k)];
} void GetSA(){
for(RI int i=;i<n;i++) X[str[i]]++;
for(RI int i=;i<=N;i++) X[i] += X[i-];
for(RI int i=n-;i>=;i--) sa[X[str[i]]--] = i;
for(RI int i = , num = ;i <= n;i++)
rk[sa[i]] = (str[sa[i]] == str[sa[i-]]?num:++num);
rk[sa[]] = ;
for(int k=;(<<k-)<=n;k++){
for(RI int i=;i<=N;i++) X[i] = ;
for(RI int i=n-(<<k-);i<n;i++) Y[i-n+(<<k-)+]=i;
for(RI int i=,j=(<<k-)+;i<=n;i++)
if(sa[i]>=(<<k-))Y[j++]=sa[i]-(<<k-);
for(RI int i=;i<n;i++) X[rk[i]]++;
for(RI int i=;i<=N;i++) X[i]+=X[i-];
for(RI int i=n;i>=;i--) sa[X[rk[Y[i]]]--] = Y[i];
int num = ; Y[sa[]] = ;
for(RI int i=;i<=n;i++) Y[sa[i]] = (chk(i,k-)?num:++num);
for(RI int i=;i<n;i++) rk[i] = Y[i];
if(num == n) break;
}
} void buildRMQ(){
for(int i=;i<=n;i++) RMQ[i][] = ht[i];
for(int i=;(<<i)<=n;i++) pw[(<<i)] = i;
for(int i=;i<=n;i++) if(!pw[i]) pw[i] = pw[i-];
for(int k=;(<<k)<=n;k++){
for(RI int i=;i<=n;i++){
if(i+(<<k-)>n) RMQ[i][k] = RMQ[i][k-];
else RMQ[i][k] = min(RMQ[i][k-],RMQ[i+(<<k-)][k-]);
}
}
} void GetHeight(){
for(RI int i=;i<n;i++){
if(i) h[i] = max(,h[i-]-);
if(rk[i] == ) {h[i] = ;continue;}
while(str[i+h[i]] == str[sa[rk[i]-]+h[i]]) h[i]++;
}
for(RI int i=;i<n;i++) ht[rk[i]] = h[i];
} int LCP(int l,int r){
l = rk[l],r = rk[r]; if(l > r) swap(l,r);l++;
int k = pw[r-l+];
return min(RMQ[l][k],RMQ[r-(<<k)+][k]);
} void update(int now,int tl,int tr,int pos,int len){
if(tl == tr){T[now] = max(T[now],len); return;}
int mid = (tl+tr)/;
if(pos <= mid) update(now<<,tl,mid,pos,len);
else update(now<<|,mid+,tr,pos,len);
T[now] = max(T[now<<],T[now<<|]);
} int Query(int now,int tl,int tr,int l,int r,int mx){
if(tl >= l && tr <= r){
if(T[now] <= mx) return -;
else{
while(tl < tr){
int mid = (tl+tr)/;
if(T[now<<|] > mx) tl = mid+,now = now*+;
else tr = mid,now<<=;
}
return tl;
}
}
if(tl > r || tr < l) return -;
int mid = (tl+tr)/;
if(mid >= r) return Query(now<<,tl,mid,l,r,mx);
if(mid < l) return Query(now<<|,mid+,tr,l,r,mx);
int z = Query(now<<|,mid+,tr,l,r,mx);
if(z!=-) return z;
else return Query(now<<,tl,mid,l,r,mx);
} vector<int> mlgb[maxn]; namespace BruteForce{
int minn[maxn<<]; pair<int,int> QueryRange(int now,int tl,int tr,int l,int r,int up,int d){
if(tl >= l && tr <= r && d == ){
if(minn[now] < up){
while(tl != tr){
int mid = (tl+tr)/;
if(minn[now<<|] < up) tl = mid+,now=now*+;
else tr = mid,now<<=;
}
return make_pair(minn[now],tl);
}else return make_pair(,);
}
if(tl >= l && tr <= r && d == ){
if(minn[now] < up){
while(tl != tr){
int mid = (tl+tr)/;
if(minn[now<<] < up) tr = mid,now<<=;
else tl = mid+,now=now*+;
}
return make_pair(minn[now],tl);
}else return make_pair(,);
}
if(tl > r || tr < l) return make_pair(,);
int mid = (tl+tr)/;
if(mid>=r) return QueryRange(now<<,tl,mid,l,r,up,d);
if(mid<l) return QueryRange(now<<|,mid+,tr,l,r,up,d);
if(d){
pair<int,int> z = QueryRange(now<<|,mid+,tr,l,r,up,d);
if(z.first) return z;
else return QueryRange(now<<,tl,mid,l,r,up,d);
}else{
pair<int,int> z = QueryRange(now<<,tl,mid,l,r,up,d);
if(z.first) return z;
else return QueryRange(now<<|,mid+,tr,l,r,up,d);
}
}
void add(int now,int tl,int tr,int pos,int dt){
if(tl == tr) {minn[now] = dt;return;}
int mid = (tl+tr)/;
if(pos <= mid) add(now<<,tl,mid,pos,dt);
else add(now<<|,mid+,tr,pos,dt);
minn[now] = min(minn[now<<],minn[now<<|]);
} void holyshit(){
memset(minn,0x3f,sizeof(minn));
for(int i=n-;i>=;i--){
int z = rk[i],p=;
add(,,n,z,i);
while(true){
pair<int,int> um = QueryRange(,,n,z+,n,p,);
if(!um.first) break;
mlgb[um.first].push_back(i);
z = um.second; p = sa[z];
}
z = rk[i],p = ;
while(true){
pair<int,int> um = QueryRange(,,n,,z-,p,);
if(!um.first) break;
mlgb[um.first].push_back(i);
z = um.second; p = sa[z];
}
} }
} void work(){
GetSA();
GetHeight();
buildRMQ(); BruteForce::holyshit();
int num = ;
for(int i=;i<n;i++){
for(int j=;j<mlgb[i].size();j++){
int z = LCP(mlgb[i][j],i);
if(a[mlgb[i][j]] >= z) continue;
a[mlgb[i][j]] = z;
update(,,n-,mlgb[i][j],z); }
while(qy[num].r == i){
int z = ,pq = qy[num].r-;
while(pq >= qy[num].l){
int hp = Query(,,n-,,pq,z);
if(hp < qy[num].l) hp = qy[num].l-;
ans[qy[num].nb] += (pq-hp)*z;
if(hp < ) break;
z = a[hp],pq = hp;
}
num++;
}
}
for(int i=;i<=q;i++) printf("%d\n",ans[i]);
} int cmp(node alpha,node beta){return alpha.r < beta.r;}
int main(){
//freopen("1.in","r",stdin);
fstread(n);fstread(q);
for(int i=;i<=n;i++){
str[i-] = getchar();
while(str[i-]!= '' && str[i-] != '') str[i-] = getchar();
}
for(int i=;i<=q;i++) fstread(qy[i].l),fstread(qy[i].r),qy[i].nb=i;
for(int i=;i<=q;i++) qy[i].l--,qy[i].r--;
sort(qy+,qy+q+,cmp);
work();
return ;
}

Luogu3732 [HAOI2017] 供给侧改革 【后缀数组】【线段树】【乱搞】的更多相关文章

  1. 【Luogu3732】[HAOI2017]供给侧改革(Trie树)

    [Luogu3732][HAOI2017]供给侧改革(Trie树) 题面 洛谷 给定一个纯随机的\(01\)串,每次询问\([L,R]\)之间所有后缀两两之间的\(LCP\)的最大值. 题解 一个暴力 ...

  2. 洛谷3732:[HAOI2017]供给侧改革——题解

    https://www.luogu.org/problemnew/show/P3732 Anihc国提高社会生产力水平.落实好以人民为中心的发展思想.决定进行供给侧结构性改革. 为了提高供给品质.你调 ...

  3. 洛谷 P3732 [HAOI2017]供给侧改革【trie树】

    参考:http://blog.csdn.net/di4covery/article/details/73065684 我以为是后缀数组+某某数据结构,结果居然是01trie!!题解说"因为是 ...

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

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

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

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

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

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

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

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

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

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

  9. Codeforces 1063F - String Journey(后缀数组+线段树+dp)

    Codeforces 题面传送门 & 洛谷题面传送门 神仙题,做了我整整 2.5h,写篇题解纪念下逝去的中午 后排膜拜 1 年前就独立切掉此题的 ymx,我在 2021 年的第 5270 个小 ...

随机推荐

  1. Java开发笔记(序)章节目录

    现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...

  2. mysql获取某个表中除了某个字段名外的所有字段名

    现一个表中有很多字段,但我不想要其中某一个字段,手打出来字段太多,不方便. SELECT GROUP_CONCAT( column_name SEPARATOR ',' ) FROM informat ...

  3. 前端零基础 --css转换--skew斜切变形 transfor 3d

    前端零基础 --css转换--skew斜切变形 transfor 3d==============重要不紧急! 重要紧急 重要不紧急 不重要紧急 不重要不紧急

  4. java工作流引擎Jflow父子流程demo

    关键字 驰骋工作流引擎 流程快速开发平台 workflow ccflow jflow  .net开源工作流 定义 一个流程A的一个节点,因工作的需要调起另外的流程B,A就叫父流程,B就叫子流程.如果流 ...

  5. VR一体机如何退出FFBM

            Fast Factory Boot Mode(FFBM)是一种半开机的模式,它的主要目的是方便工厂测试,提高生产效率.正常情况下终端用户是不会碰到的.但售后的同学最近连续收到几台客户退 ...

  6. 从0开始的Python学习011模块

    简介 你已经学习了如何在你的程序中定义一次函数而重用代码.如果你想要在其他程序中重用很多函数,那么你该如何编写程序呢?你可能已经猜到了,答案是使用模块.模块基本上就是一个包含了所有你定义的函数和变量的 ...

  7. scrapy安装失败:error:Microsoft Visual C++ 14.0 is reuired.及同类型安装问题解决办法

    今天在安装scrapy的时候(pip install Scrapy),出现了如下错误: building 'twisted.test.raiser' extensionerror: Microsoft ...

  8. DES加密算法应用:分组加密模式

    通常,大多数的分组加密算法都是把数据按照64位分组的方式进行加密和解密.但是几乎所有的加密工作所涉及的数据量都远远大于64位,因此就需要不断地重复加密过程,直到处理完所有的分组.这种分组加密中所涉及的 ...

  9. 对const的总结与思考

    今天偶然想起const这一关键字,再加之以前几种const修饰指针方式让我印象深刻(混淆不清),重新回顾了一下,自己对这个关键词也又有了更加深刻的理解,所以总结一下. 一.const的定义 const ...

  10. 解决CSDN需要登录才能看全文

    本来今天学习遇到一些问题,在网上翻着博客,突然在csdn里就提示要登录才能看全文. 看了下页面源码博客内容已经拿到本地了,只是加了一层罩,也是挺无语的,暂时先用这种方法解决吧: (function() ...