CF1073G Yet Another LCP Problem
题意简述:给出 \(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的更多相关文章
- cf1073G Yet Another LCP Problem (SA+权值线段树)
反正先求一遍sa 然后这个问题可以稍微转化一下 默认比较A.B数组中元素的大小都是比较它们rank的大小,毕竟两个位置的LCP就是它们rank的rmq 然后每次只要求B[j]>=A[i]的LCP ...
- CF1073G Yet Another LCP Problem 后缀自动机 + 虚树 + 树形DP
题目描述 记 $lcp(i,j)$ 表示 $i$ 表示 $i$ 这个后缀和 $j$ 这个后缀的最长公共后缀长度给定一个字符串,每次询问的时候给出两个正整数集合 $A$ 和 $B$,求$\sum_{i\ ...
- 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即可.(感 ...
- Codeforces 1073G Yet Another LCP Problem $SA$+单调栈
题意 给出一个字符串\(s\)和\(q\)个询问. 每次询问给出两个长度分别为\(k,l\)的序列\(a\)和序列\(b\). 求\(\sum_{i=1}^{k}\sum_{j=1}^{l}lcp(s ...
- 虚树总结&题单&简要题解
简介 虚树,即剔除所有无关结点,只保留询问点和询问点的相关结点(两两之间的LCA),建一棵新树,这棵新树就是虚树.通过虚树,可以有效的减小询问(甚至修改)的复杂度.设询问点的个数是\(k\),那么建虚 ...
- SAM 做题笔记(各种技巧,持续更新,SA)
SAM 感性瞎扯. 这里是 SAM 做题笔记. 本来是在一篇随笔里面,然后 Latex 太多加载不过来就分成了两篇. 标 * 的是推荐一做的题目. trick 是我总结的技巧. I. P3804 [模 ...
- Codeforces-Educational Codeforces Round 53题解
写之前,先发表下感慨:好久没写题解了,也许是因为自己越来越急利了,也可以说是因为越来越懒了. A. Diverse Substring 直接找一找有没有相邻的两个不同的字符即可. B. Vasya a ...
- Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢"Grunt"大佬的细心讲解)
layout: post title: Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢"Grunt"大佬的细 ...
- [CF1073G]LCP问题
题意:给一个长n的字符串S,q组询问,每组给两个集合A,B.求集合A中的点和集合B中的点所有组合情况的lcp的和. 思路: 好像比较常规,可是代码能力差还是调了1.5h.主要还是虚树板子不熟(加入的时 ...
随机推荐
- [no code][scrum meeting] Beta 6
$( "#cnblogs_post_body" ).catalog() 例会时间:5月19日11:30,主持者:黎正宇 下次例会时间:5月20日11:30,主持者:彭毛小民 一.工 ...
- [Beta]the Agiles Scrum Meeting 7
会议时间:2020.5.21 20:00 1.每个人的工作 今天已完成的工作 成员 已完成的工作 issue yjy 暂无 tq 新增功能:添加.选择.展示多个评测机,对新增功能进行测试 评测部分增加 ...
- BUAA_2020_软件工程_软件案例分析作业
项目 内容 这个作业属于那个课程 班级博客 这个作业的要求在哪里 作业要求 我在这个课程的目标是 学习掌握软件工程的相关知识 这个作业在哪个具体方面帮我实现目标 通过对具体软件案例的分析学习软件工程 ...
- 架构师之路-https底层原理
引子 先说说我对架构师的理解.从业务能力上,需要的是发现问题和解决问题的能力:从团队建设上,需要的是能培养团队的业务能力:从项目管理上,把控好整个项目和软件产品的全生命周期. 我搜索了一下架构师的培训 ...
- Photoshop cc 绿色版 最新版 下载
Photoshop cc 绿色版 下载 Photoshop cc 绿色版 最新版下载百度网盘下载 Photoshop 下载提取码: dh6z 作为一个程序员, 不懂点基本的作图都不配"新时代 ...
- hdu 3635 Dragon Balls(并查集)
题意: N个城市,每个城市有一个龙珠. 两个操作: 1.T A B:A城市的所有龙珠转移到B城市. 2.Q A:输出第A颗龙珠所在的城市,这个城市里所有的龙珠个数,第A颗龙珠总共到目前为止被转移了多少 ...
- Redis Stream类型的使用
一.背景 最近在看redis这方面的知识,发现在redis5中产生了一种新的数据类型Stream,它和kafka的设计有些类似,可以当作一个简单的消息队列来使用. 二.redis中Stream类型的特 ...
- 【JAVA】编程(2)---时间管理
作业要求: 定义一个名为 MyTime 的类,其中私有属性包括天数,时,分,秒:定义一个可以初始化时,分,秒的构造方法,并对初始化数值加以限定,以防出现bug:定义一个方法,可以把第几天,时,分,秒打 ...
- Java 中的关键字
Java 中有多少个关键字,有大小写之分吗? Java 中有 48 个关键字在使用 + 两个保留关键字未使用,共 50 个关键字. Java 关键字全部都由是小写组成. Java 中保留关键字分别是哪 ...
- python 字符串和时间格式(datetime)相互转换-
2019-03-17 11:00:00格式转化 import datetime # str转时间格式: dd = '2019-03-17 11:00:00' dd = datetime.datetim ...