cf1073G Yet Another LCP Problem (SA+权值线段树)
反正先求一遍sa
然后这个问题可以稍微转化一下
默认比较A、B数组中元素的大小都是比较它们rank的大小,毕竟两个位置的LCP就是它们rank的rmq
然后每次只要求B[j]>=A[i]的LCP(B[j],A[i]),然后再求A[j]>B[i]的LCP(A[j],B[i])即可
这两个其实是差不多的,下面只说B[j]>=A[i]的怎么算
排序以后从后往前推着做(当然从前往后也行)
用一个权值线段树记下来LCP(A[i],B[j])==x的B[j]的数量、以及这个数量*x的和
然后考虑怎么把它从A[i]转移到A[i-1]
其实就是对于每个j给LCP(A[i],B[j])和LCP(A[i-1],A[i])取个min,放到权值线段树上,就是把大于LCP(A[i-1],A[i])的都删掉,然后在LCP(A[i-1],A[i])处加上刚才删掉的个数
所以我推着做的时候,先来取个min,然后把新来的B[j]加到线段树里,每做一个A[i]统计一下线段树整体的和即可
#include<bits/stdc++.h>
#define pa pair<int,int>
#define CLR(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=4e5+; inline ll rd(){
ll x=;char c=getchar();int neg=;
while(c<''||c>''){if(c=='-') neg=-;c=getchar();}
while(c>=''&&c<='') x=x*+c-'',c=getchar();
return x*neg;
} int N,M,Q;
char s[maxn];
int sa[maxn],rnk[maxn],hei[maxn],rank1[maxn],tmp[maxn],cnt[maxn];
int st[maxn][]; inline void getsa(){
int i,j=,k;
for(i=;i<=N;i++) cnt[s[i]]=;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) rnk[i]=cnt[s[i]]; for(k=;j!=N;k<<=){
memset(cnt,,sizeof(cnt));
for(i=;i<=N;i++) cnt[rnk[i+k>N?:i+k]]++;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) tmp[cnt[rnk[i+k>N?:i+k]]--]=i;
memset(cnt,,sizeof(cnt));
for(i=;i<=N;i++) cnt[rnk[i]]++;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) sa[cnt[rnk[tmp[i]]]--]=tmp[i];
memcpy(rank1,rnk,sizeof(rank1));
rnk[sa[]]=j=;
for(i=;i<=N;i++){
if(rank1[sa[i]]!=rank1[sa[i-]]||rank1[sa[i]+k>N?:sa[i]+k]!=rank1[sa[i-]+k>N?:sa[i-]+k]) j++;
rnk[sa[i]]=j;
}M=j;
}
for(i=;i<=N;i++) sa[rnk[i]]=i;
} inline void geth(){
for(int i=,j=;i<=N;i++){
if(rnk[i]==) continue;
if(j) j--;
int x=sa[rnk[i]-];
while(x+j<=N&&i+j<=N&&s[x+j]==s[i+j]) j++;
hei[rnk[i]]=j;
}
} inline void getst(){
for(int i=N;i;i--){
st[i][]=hei[i];
for(int j=;st[i+(<<(j-))][j-];j++){
st[i][j]=min(st[i][j-],st[i+(<<(j-))][j-]);
}
}
} inline int rmq(int l,int r){
if(l>r) return N-sa[r]+;
int x=log2(r-l+);
return min(st[l][x],st[r-(<<x)+][x]);
} ll sum[maxn<<],siz[maxn<<];
bool laz[maxn<<]; inline void update(int p){sum[p]=sum[p<<]+sum[p<<|],siz[p]=siz[p<<]+siz[p<<|];}
inline void pushdown(int p){
if(!laz[p]) return;
int a=p<<,b=p<<|;
sum[a]=siz[a]=,laz[a]=;
sum[b]=siz[b]=,laz[b]=;
laz[p]=;
}
int erase(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
int re=siz[p];
siz[p]=sum[p]=;laz[p]=;
pushdown(p);
return re;
}else{
pushdown(p);
int m=l+r>>,re=;
if(x<=m) re=erase(p<<,l,m,x,y);
if(y>=m+) re+=erase(p<<|,m+,r,x,y);
update(p);
return re;
}
}
void add(int p,int l,int r,int x,int y){
if(l==r){
siz[p]+=y,sum[p]+=1ll*y*l;
}else{
pushdown(p);
int m=l+r>>;
if(x<=m) add(p<<,l,m,x,y);
else add(p<<|,m+,r,x,y);
update(p);
}
} inline bool cmp(int a,int b){return rnk[a]<rnk[b];} int A[maxn],B[maxn];
ll solve(int k,int l){
sort(A+,A+k+,cmp);sort(B+,B+l+,cmp);
ll re=;
for(int i=k,j=l;i;i--){
if(i!=k){
int x=rmq(rnk[A[i]]+,rnk[A[i+]]),t=erase(,,N,x+,N);
add(,,N,x,t);
}
for(;rnk[B[j]]>=rnk[A[i]]&&j;j--)
add(,,N,rmq(rnk[A[i]]+,rnk[B[j]]),);
re+=sum[];
}
erase(,,N,,N);
for(int i=k,j=l;j;j--){
if(j!=l){
int x=rmq(rnk[B[j]]+,rnk[B[j+]]),t=erase(,,N,x+,N);
add(,,N,x,t);
}
for(;rnk[A[i]]>rnk[B[j]]&&i;i--)
add(,,N,rmq(rnk[B[j]]+,rnk[A[i]]),);
re+=sum[];
}
erase(,,N,,N);
return re;
} int main(){
//freopen(".in","r",stdin);
int i,j,k;
N=rd(),Q=rd();
scanf("%s",s+);
M=;
getsa();geth();getst();
for(i=;i<=Q;i++){
int a=rd(),b=rd();
for(j=;j<=a;j++)
A[j]=rd();
for(j=;j<=b;j++)
B[j]=rd();
printf("%I64d\n",solve(a,b));
}
return ;
}
cf1073G Yet Another LCP Problem (SA+权值线段树)的更多相关文章
- BZOJ4627 前缀和 + 权值线段树
https://www.lydsy.com/JudgeOnline/problem.php?id=4627 题意:求序列中和在L到R之间的字串种数. 要求的是和的范围,我们可以考虑先求一个前缀和pre ...
- F - 回转寿司 (权值线段树)
题目链接:https://cn.vjudge.net/contest/281960#problem/F 题目大意:中文题目 具体思路:权值线段树,我们每次寻找的是满足 (i<j) L< ...
- BZOJ 4605 崂山白花蛇草水(权值线段树+KD树)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4605 [题目大意] 操作 1 x y k 表示在点(x,y)上放置k个物品, 操作 2 ...
- HDU 6464 免费送气球 【权值线段树】(广东工业大学第十四届程序设计竞赛)
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6464 免费送气球 Time Limit: 2000/1000 MS (Java/Others) M ...
- hdu 5592 ZYB's Premutation (权值线段树)
最近在线段树的世界里遨游,什么都能用线段树做,这不又一道权值线段树了么. ZYB's Premutation Time Limit: 2000/1000 MS (Java/Others) Mem ...
- [bzoj 2733]启发式合并权值线段树
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 平衡树待学习.从一个博客学到了合并权值线段树的姿势:http://blog.csdn ...
- HDU 6464 权值线段树 && HDU 6468 思维题
免费送气球 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submi ...
- D. Restore Permutation(权值线段树)
D. Restore Permutation time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并)
layout: post title: HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并) author: "luowentaoaa&quo ...
随机推荐
- Oracle 创建外部表
Oracle 外部表能迅速的将海量的数据导入到数据库里面,外部表的创建使用步骤如下: 1 创建一个Directory:必须用sys用户创建,用户存放外部数据文件. create directory D ...
- 120. 单词接龙 (BFS)
描述 给出两个单词(start和end)和一个字典,找到从start到end的最短转换序列 比如: 每次只能改变一个字母. 变换过程中的中间单词必须在字典中出现. 如果没有转换序列则返回0. 所有单词 ...
- 47.Majority Element I & II
Majority Element I 描述 给定一个整型数组,找出主元素,它在数组中的出现次数严格大于数组元素个数的二分之一. You may assume that the array is non ...
- BPM与OA的区别
近年来,随着计算机技术的发展和互联网时代的到来,我们已经进入了信息时代,也称为数字化时代,在这数字化的时代里,企业的经营管理都受到了极大的挑战.从上世纪90年代起至今,企业的信息化工作开展的如火如荼, ...
- django之路由层
一 Django中路由的作用 二 简单的路由配置 三 有名分组 四 路由分发 五 反向解析 六 名称空间 七 django2.0版的path 一 Django中路由的作用 URL配置(URLconf) ...
- thymeleaf 引入公共html注意事项
详细连接https://blog.csdn.net/u010260737/article/details/83616998 每个页面都会用到分页.html或者头部.html.尾部.html,在其他页面 ...
- python设计模式第二十三天【状态模式】
1.应用场景 (1)通过改变对象的内部状态从而改变对象的行为,一般表现为状态的顺序执行 2.代码实现 #!/usr/bin/env python #!_*_ coding:UTF-8 _*_ from ...
- Python——Flask框架——程序的基本结构
一.安装 pip install flask 二.初始化 from flask import Flask app = Flash(__name__) 三.路由:处理URL和函数之间的关系的程序称为路由 ...
- 学习 Spring (十六) AOP API
Spring入门篇 学习笔记 Spring AOP API 是 Spring 1.2 历史用法,现在仍然支持 这是 Spring AOP 基础,现在的用法也是基于历史的,只是更简便了 Pointcut ...
- DotNetty 实现 Modbus TCP 系列 (二) ModbusFunction 类图及继承举例
本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议 ModbusFunction 类图如下: 如前文所述,所有请求/相应的 PDU 均继承自 ModbusFunction, ...