题目传送门

题意简述:给出 \(s\),多次询问给出长度分别为 \(k,l\) 的序列 \(a,b\),求 \(\sum_{i=1}^k\sum_{j=1}^l\mathrm{LCP}(s[a_i:n],s[b_j:n])\)。

Yet Another 套路题。


如果你做过 P4248 [AHOI2013]差异 应该可以很快秒掉这题。

我们先对 \(s\) 进行后缀排序,求出 height 数组,并将 \(a_i,b_i\) 替换为 \(rk_{a_i},rk_{b_i}\),那么题目就变为 \(\sum_{i=1}^k\sum_{j=1}^l\min_{k=\min(a_i,b_i)+1}^{\max(a_i,b_i)}ht_k\)。

预处理出 \(ht\) 的 ST 表以 \(\mathcal{O}(1)\) RMQ。然后使用类似上面那题的套路,单调栈求出答案。

具体地,我们要从小到大考虑 \(a,b\) 中的每一个位置 \(pos\)(注意这里的位置指的是排名,因为前面用 \(rk_a,rk_b\) 替换了 \(a,b\)),求出在它前面的所有位置 \(p_i\ (p_i\leq pos)\)(注意这里可能取到等于号,因为 \(a,b\) 中可能有相同的位置)中,与它类型不同的(即若 \(pos\) 是 \(b\) 中的位置,则 \(p_i\) 应该是 \(a\) 中的)所有位置 \(+1\) 后与它的区间 \(ht\) 最小值之和:对 \(a,b\) 分别维护一个单调栈 \(A,B\)。先用 \(val=\min_{i=pre+1}^{pos} ht_i\) 更新两个单调栈 \(A,B\)(\(pre\) 是上一次考虑的 \(pos\)),如果 \(pre=pos\) 则用 \(val=n-sa_{pos}+1\) 更新,需要注意的是这里的更新不是将一个数压入单调栈 \(A\) 或 \(B\),因为这个数是不算贡献的(如果算贡献,会导致计算了同属于 \(a\) 或同属于 \(b\) 的两个位置之间的重复贡献),相当于我们压入了一个宽为 \(0\),高为 \(val\) 的矩形;然后,若当前位置属于 \(a\),则计算单调栈 \(B\) 中的 “矩形面积和”,否则计算 \(A\) 中的矩形面积和;最后,若当前位置属于 \(a\),将 \(n-sa_{pos}+1\) 压入单调栈 \(A\),否则将其压入 \(B\),相当于我们压入了一个宽为 \(1\),高为 \(n-sa_{pos}+1\) 的矩形。所有计算出的 “矩形面积和” 之和即为答案。

时间复杂度 \(\mathcal{O}(n\log n+\sum k\log\sum k+\sum l\log \sum l)\)(后面的 \(\log\) 是排序所需的 \(\log\))。

CF1073G 代码
/*
Powered by C++11.
Author : Alex_Wei.
*/ #include <bits/stdc++.h>
using namespace std; #define ll long long const int N=2e5+5;
const int K=18; struct MonotoneStack{
ll stc[N],l[N],top,sum;
void modify(int val){
int len=0;
while(top&&stc[top]>=val)sum-=stc[top]*l[top],len+=l[top--];
sum+=len*val,stc[++top]=val,l[top]=len;
} void push(int val){
stc[++top]=val,l[top]=1,sum+=val;
}
}ta,tb; int n,q,k,l,a[N],b[N];
ll sta[N],stb[N];
char s[N];
struct SA{
int sa[N],rk[N<<1],ork[N<<1],ht[N];
int buc[N],id[N],px[N],mi[N][K];
bool cmp(int a,int b,int w){
return ork[a]==ork[b]&&ork[a+w]==ork[b+w];
} void build(){
int m=1<<7,p=0;
for(int i=1;i<=n;i++)buc[rk[i]=s[i]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n;i;i--)sa[buc[rk[i]]--]=i;
for(int w=1;w<=n;w<<=1,m=p,p=0){
for(int i=n;i>n-w;i--)id[++p]=i;
for(int i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
for(int i=0;i<=m;i++)buc[i]=0;
for(int i=1;i<=n;i++)buc[px[i]=rk[id[i]]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n;i;i--)sa[buc[px[i]]--]=id[i];
for(int i=1;i<=n;i++)ork[i]=rk[i]; p=0;
for(int i=1;i<=n;i++)rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
} for(int i=1,k=0;i<=n;i++){
if(k)k--;
while(s[i+k]==s[sa[rk[i]-1]+k])k++;
ht[rk[i]]=k;
} for(int j=0;j<K;j++)
for(int i=1;i<=n;i++)
if(j)mi[i][j]=min(mi[i][j-1],mi[i+(1<<j-1)][j-1]);
else mi[i][j]=ht[i];
} int query(int l,int r){
if(l>r)swap(l,r);
if(l==r)return n-sa[l]+1;
if(l+1==r)return ht[r];
int d=log2(r-(l++));
return min(mi[l][d],mi[r-(1<<d)+1][d]);
} ll run(ll k,ll l){
for(int i=1;i<=k;i++)a[i]=rk[a[i]];
for(int i=1;i<=l;i++)b[i]=rk[b[i]];
sort(a+1,a+k+1),sort(b+1,b+l+1);
ll lp=1,rp=1,ans=0,pre=0;
while(lp<=k||rp<=l){
int tp,pos;
if(lp>k||rp<=l&&b[rp]<a[lp])tp=2,pos=b[rp++];
else tp=1,pos=a[lp++];
int val=query(pre,pos);
ta.modify(val),tb.modify(val);
if(tp==1)ans+=tb.sum,ta.push(n-sa[pos]+1);
else ans+=ta.sum,tb.push(n-sa[pos]+1);
pre=pos;
} return ans;
}
}sa; int main(){
scanf("%d%d%s",&n,&q,s+1),sa.build();
while(q--){
scanf("%d%d",&k,&l);
for(int i=1;i<=k;i++)scanf("%d",&a[i]);
for(int i=1;i<=l;i++)scanf("%d",&b[i]);
cout<<sa.run(k,l)<<endl;
ta.top=tb.top=ta.sum=tb.sum=0;
}
return 0;
}

CF1073G Yet Another LCP Problem的更多相关文章

  1. cf1073G Yet Another LCP Problem (SA+权值线段树)

    反正先求一遍sa 然后这个问题可以稍微转化一下 默认比较A.B数组中元素的大小都是比较它们rank的大小,毕竟两个位置的LCP就是它们rank的rmq 然后每次只要求B[j]>=A[i]的LCP ...

  2. CF1073G Yet Another LCP Problem 后缀自动机 + 虚树 + 树形DP

    题目描述 记 $lcp(i,j)$ 表示 $i$ 表示 $i$ 这个后缀和 $j$ 这个后缀的最长公共后缀长度给定一个字符串,每次询问的时候给出两个正整数集合 $A$ 和 $B$,求$\sum_{i\ ...

  3. Educational Codeforces Round 53 (Rated for Div. 2)G. Yet Another LCP Problem

    题意:给串s,每次询问k个数a,l个数b,问a和b作为后缀的lcp的综合 题解:和bzoj3879类似,反向sam日神仙...lcp就是fail树上的lca.把点抠出来建虚树,然后在上面dp即可.(感 ...

  4. Codeforces 1073G Yet Another LCP Problem $SA$+单调栈

    题意 给出一个字符串\(s\)和\(q\)个询问. 每次询问给出两个长度分别为\(k,l\)的序列\(a\)和序列\(b\). 求\(\sum_{i=1}^{k}\sum_{j=1}^{l}lcp(s ...

  5. 虚树总结&题单&简要题解

    简介 虚树,即剔除所有无关结点,只保留询问点和询问点的相关结点(两两之间的LCA),建一棵新树,这棵新树就是虚树.通过虚树,可以有效的减小询问(甚至修改)的复杂度.设询问点的个数是\(k\),那么建虚 ...

  6. SAM 做题笔记(各种技巧,持续更新,SA)

    SAM 感性瞎扯. 这里是 SAM 做题笔记. 本来是在一篇随笔里面,然后 Latex 太多加载不过来就分成了两篇. 标 * 的是推荐一做的题目. trick 是我总结的技巧. I. P3804 [模 ...

  7. Codeforces-Educational Codeforces Round 53题解

    写之前,先发表下感慨:好久没写题解了,也许是因为自己越来越急利了,也可以说是因为越来越懒了. A. Diverse Substring 直接找一找有没有相邻的两个不同的字符即可. B. Vasya a ...

  8. Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢"Grunt"大佬的细心讲解)

    layout: post title: Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢"Grunt"大佬的细 ...

  9. [CF1073G]LCP问题

    题意:给一个长n的字符串S,q组询问,每组给两个集合A,B.求集合A中的点和集合B中的点所有组合情况的lcp的和. 思路: 好像比较常规,可是代码能力差还是调了1.5h.主要还是虚树板子不熟(加入的时 ...

随机推荐

  1. vue.$nextTick实现原理

    源码: const callbacks = [] let pending = false function flushCallbacks () { pending = false const copi ...

  2. MongoDB中如何优雅地删除大量数据

    删除大量数据,无论是在哪种数据库中,都是一个普遍性的需求.除了正常的业务需求,我们需要通过这种方式来为数据库"瘦身". 为什么要"瘦身"呢? 表的数据量到达一定 ...

  3. CSP-S2021幽寂

    不管怎么说,这次比赛考的比这一段时间以来的模拟赛都难看 难受,但是也不想太表现出来,所以更难受.... 有点害怕会退役...... Day -6 前一天晚上回宿舍的时候和\(zxs\)一路,聊的过程中 ...

  4. 21.10.18 test

    可可大神出题,四款有趣的游戏推荐,第四个好玩/se T1 loopers \(\color{green}{100}\) 考虑钦定 \(a_1,a_i\) 的位置,固定左边一坨,那么剩下的一坨的 \(\ ...

  5. 【行人惯性导航】关于行人导航中IMU位姿推导的知识点及相关代码

    IMU姿态惯性推导 最近从事行人惯性导航的研究,本人也是一个小白,其中看了很多文献,有很多个人思考很费时间的地方,撰写此随笔的目的不仅是给自己做一个笔记,也是给各位有需要的仁兄一点个人理解. 本文只关 ...

  6. Python ImportError: cannot import name ABC

    Python 3.5.2 测试可以运行 import sys from abc import ABC,abstractmethod class MyBase(ABC): @abstractmethod ...

  7. Luogu P3758 [TJOI2017]可乐 | 矩阵乘法

    题目链接 让我们先来思考一个问题,在一张包含$n$个点的图上,如何求走两步后从任意一点$i$到任意一点$j$的方案数. 我们用$F_p(i,j)$来表示走$p$步后从$i$到$j$的方案数,如果存储原 ...

  8. sudo user1账号获得管理员root的权限

    user1虽然有sudo权限,但不是真正的root权限,修改内核参数之类的就做不了 但是有sudo权限就可以添加账号,以下添加了admin账号与root账号一样的权限 useradd -u 0   - ...

  9. Loto实践干货(8)loto示波器在LED台灯调光问题维修中的应用案例

    Loto实践干货(8)loto示波器在LED台灯调光问题维修中的应用案例 一位客户最近觉得觉得他的LED台灯好闪, 于是拆了看看,里面的控制板是这样的: 干掉双色调光功能,只调亮度的话闪烁的状况能好转 ...

  10. ELK集群之kafka(7)

    原理待补充: kafka依赖于zookeeper集群. 都是基于java 由于源码安装jdk 未声明bin下java 在各自server配置文件中声明 JAVA_HOME=/usr/local/jdk ...