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 ...
随机推荐
- [转帖]前端-chromeF12 谷歌开发者工具详解 Sources篇
前端-chromeF12 谷歌开发者工具详解 Sources篇 原贴地址:https://blog.csdn.net/qq_39892932/article/details/82498748 cons ...
- python爬虫scrapy之如何同时执行多个scrapy爬行任务
背景: 刚开始学习scrapy爬虫框架的时候,就在想如果我在服务器上执行一个爬虫任务的话,还说的过去.但是我不能每个爬虫任务就新建一个项目吧.例如我建立了一个知乎的爬行任务,但是我在这个爬行任务中,写 ...
- WPF当属性值改变时利用PropertyChanged事件来加载动画
在我们的程序中,有时我们需要当绑定到UI界面上的属性值发生变化从而引起数据更新的时候能够加载一些动画,从而使数据更新的效果更佳绚丽,在我们的程序中尽量将动画作为一种资源放在xaml中,而不是在后台中通 ...
- 从主机A得到id_rsa.pub文件,在主机B创建用户danny加入该文件,实现主机A免密登录主机B
操作步骤: 1.主机A 生成公钥id_rsa.pub文件并导出该文件 root@mytest:~# ssh-keygen Generating public/private rsa key pair. ...
- scrapy的一些容易忽视的点(模拟登陆,传递item等)
scrapy爬虫注意事项 一.item数据只有最后一条 这种情况一般存在于对标签进行遍历时,将item对象放置在了for循环的外部.解决方式:将item放置在for循环里面. 二.item字段传递 ...
- 销售合同金额数据从Excel导入
一.业务需求 1.新增了销售合同金额的字段,但是老数据没有这个字段:所以销售合同金额从销售合同附件的各品种金额之和. 2.制作好excel字段模板,将此模板发送给销售业务部门来统计并完成excel表格 ...
- nginx worker_processes 配置
搜索到原作者的话:As a general rule you need the only worker with large number ofworker_connections, say 10,0 ...
- qtp 自动货测试桌面程序-笔记(使用函数)
新建-function 写入函数 rem 关闭出现错误窗口Function checkExist() If Window("出现错误").WinObject("确定&qu ...
- ES 6 系列 - 变量声明
let 和 const let 声明 (一)基本用法 let 声明的变量只在块级作用域内有效,出了该块则报错,最常见且适合的地方在 for 循环中: var a = []; for (var i = ...
- CountDownLatch(三)
CountDownLatch简介 (1)用于解决什么问题? 在并发编程的场景中,最常见的一个case是某个任务的执行,需要等到多个线程都执行完毕之后才可以进行,CountDownLatch可以很好解决 ...