P5161 WD与数列

可以想到原条件是一个差分形式,所以我们对原数组差分。然后发现答案其实就是 \(\sum_{i<j} \min(lcp(i+1,j+1)+1,j-i)\)。

这个东西先跑 SA,然后建笛卡尔树。

考虑对于一个区间,其值为 \(x\)。那么相当于是求 \(\sum_{l\in S,r\in T} \min(|sa_{l}-sa_{r}|,x)\)。

笛卡尔树的一个性质是:较小的区间之和不超过 \(O(n\log n)\)。所以直接暴力枚举较小区间,假设为 \(l\),那么对于右边相当于是求区间内 \(\le\) 某个数的个数,我们显然可以把询问按照 \(x\) 离线下来,从小到大做。或者直接开主席树,都是 \(O(n\log^2n)\) 的。这题略微卡常,注意实现

  • 好像存在 \(O(n\log n)\) 的做法。
#include<bits/stdc++.h>
using namespace std; typedef long long ll;
const int maxt=1e7, maxn=3e5+5; int n,m;
int sa[maxn], rk[maxn], cnt[maxn], tp[maxn], height[maxn], lg[maxn], w[maxn][20];
int s[maxn], a[maxn]; void basesort(){
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++) cnt[rk[i]]++;
for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--) sa[cnt[rk[tp[i]]]--]=tp[i];
return ;
} void SuffixSort() {
for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
basesort();
for(int w=1,p=0;p<n;m=p,w<<=1) {
p=0;
for(int i=1;i<=w;i++) tp[++p]=n-w+i;
for(int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
basesort();
for(int i=1;i<=n;++i) swap(tp[i],rk[i]);
rk[sa[1]]=1;
p=1;
for(int i=2;i<=n;i++) {
if(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w]) rk[sa[i]]=p;
else rk[sa[i]]=++p;
}
}
return ;
} int lcp(int l,int r) {
int k=lg[r-l];
return min(w[l+1][k],w[r-(1<<k)+1][k]);
} void init() {
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) s[i]=a[i]-a[i-1];
for(int i=1;i<=n;i++) a[i]=s[i];
sort(a+1,a+n+1); m=unique(a+1,a+n+1)-a-1;
for(int i=1;i<=n;i++) s[i]=lower_bound(a+1,a+m+1,s[i])-a;
SuffixSort();
int k=0;
for(int i=1;i<=n;i++) {
if(k) --k;
int j=sa[rk[i]-1];
while(i+k<=n&&s[i+k]==s[j+k]) ++k;
height[rk[i]]=k;
}
lg[1]=0;
for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++) w[i][0]=height[i];
for(int j=1;(1<<j)<=n;j++) {
for(int i=1;i+(1<<j)-1<=n;i++) {
w[i][j]=min(w[i][j-1],w[i+(1<<j-1)][j-1]);
}
}
return ;
} int tl[maxn], tr[maxn]; int tot;
int ls[maxt], rs[maxt], rt[maxn];
ll sum[maxt], sum2[maxt]; void build(int &x,int l,int r) {
x=++tot;
if(l==r) return ;
int mid=l+r>>1;
build(ls[x],l,mid);
build(rs[x],mid+1,r);
return ;
} void updata(int &x,int x2,int p,int c,int l,int r) {
x=++tot;
sum[x]=sum[x2]+p*c, sum2[x]=sum2[x2]+c, ls[x]=ls[x2], rs[x]=rs[x2];
if(l==r) return ;
int mid=l+r>>1;
if(p<=mid) updata(ls[x],ls[x2],p,c,l,mid);
else updata(rs[x],rs[x2],p,c,mid+1,r);
return ;
} pair<ll,ll> operator + (pair<ll,ll> x,pair<ll,ll> y) {
return {x.first+y.first,x.second+y.second};
} pair<ll,ll> query(int x1,int x2,int L,int R,int l,int r) {
if(L>R) return {0,0};
if(L<=l&&r<=R) return {sum[x2]-sum[x1],sum2[x2]-sum2[x1]};
int mid=l+r>>1;
pair<ll,ll> res={0,0};
if(L<=mid) res=res+query(ls[x1],ls[x2],L,R,l,mid);
if(mid<R) res=res+query(rs[x1],rs[x2],L,R,mid+1,r);
return res;
} int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
init();
vector<int> vec;
for(int i=2;i<=n;i++) {
while(vec.size()&&height[vec.back()]>=height[i]) {
tr[vec.back()]=i;
vec.pop_back();
}
if(vec.size()) tl[i]=vec.back();
else tl[i]=1;
vec.push_back(i);
}
while(vec.size()) tr[vec.back()]=n+1, vec.pop_back();
build(rt[0],1,n);
for(int i=1;i<=n;i++) updata(rt[i],rt[i-1],sa[i],(sa[i]!=1),1,n);
ll ans=0;
for(int i=2;i<=n;i++) {
int l=tl[i], r=tr[i]; --r;
int x=height[i]+1;
if(i-l<=r-i+1) {
for(int j=l;j<i;j++) {
int v=sa[j];
if(v==1) continue;
ll sum=r-i+1-(i<=rk[1]&&rk[1]<=r);
pair<ll,ll> res=query(rt[i-1],rt[r],max(v-x,1),v,1,n);
ans+=res.second*v-res.first, sum-=res.second;
res=query(rt[i-1],rt[r],v+1,min(v+x,n),1,n);
ans+=res.first-res.second*v, sum-=res.second;
ans+=sum*x;
}
}else {
for(int j=i;j<=r;j++) {
int v=sa[j];
if(v==1) continue;
ll sum=i-l-(l<=rk[1]&&rk[1]<i);
pair<ll,ll> res=query(rt[l-1],rt[i-1],max(v-x,1),v,1,n);
ans+=res.second*v-res.first, sum-=res.second;
res=query(rt[l-1],rt[i-1],v+1,min(v+x,n),1,n);
ans+=res.first-res.second*v, sum-=res.second;
ans+=sum*x;
}
}
}
cout<<ans+n-1<<'\n';
return 0;
}

题解 WD与数列的更多相关文章

  1. luogu P5161 WD与数列 SAM 线段树合并 启发式合并

    LINK:WD与数列 这道题可谓妙绝 我明白了一个增量统计的原理. 原本的想法是:差分之后 显然长度为1的单独统计 长度为2的以及更多就是字符串之间的匹配问题了. 对差分序列建立SAM 由于第一个是一 ...

  2. 【LUOGU???】WD与数列 sam 启发式合并

    题目大意 给你一个字符串,求有多少对不相交且相同的子串. 位置不同算多对. \(n\leq 300000\) 题解 先把后缀树建出来. DFS 整棵树,维护当前子树的 right 集合. 合并两个集合 ...

  3. 『题解』LibreOJ6277 数列分块入门 1

    更好的阅读体验 Portal Portal1: LibreOJ Description 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间加法,单点查值. Input 第一行输入一个数字 ...

  4. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  5. 【LGP5161】WD与数列

    题目 也是可以用\(SAM\)来做的 我们发现要求原串不相交,那么就要求在差分序列里不相交并且不相邻 考虑一下\(SAM\),暴力做法自然是对每一个节点统计其所有\(endpos\)的影响 既然这样我 ...

  6. P5161 WD与数列(后缀自动机+线段树合并)

    传送门 没想出来→_→ 首先不难看出要差分之后计算不相交也不相邻的相等子串对数,于是差分之后建SAM,在parent树上用线段树合并维护endpos集合,然后用启发式合并维护一个节点对另一个节点的贡献 ...

  7. hdu4970 Killing Monsters (差分数列)

    2014多校9 1011 http://acm.hdu.edu.cn/showproblem.php?pid=4970 Killing Monsters Time Limit: 2000/1000 M ...

  8. CSP-S考前各种idea题解乱堆

    快要考试了我还是这么菜. 已经没有心思维护我的博客了.开一篇博文吧.可能会记得很乱. 这也许是我OI生涯的最后一篇博文了?? 肯定很长很长. 不可能的.谁知道什么时候我心态恢复就把上面两句话删掉开始在 ...

  9. 2019 第十届蓝桥杯大赛软件类省赛 Java A组 题解

    2019 第十届蓝桥杯大赛软件类省赛 Java A组 试题A 题解 ​ 题目最后一句贴心的提示选手应该使用 long (C/C++ 应该使用 long long). ​ 本题思路很直白,两重循环.外层 ...

  10. 5806 NanoApe Loves Sequence Ⅱ(尺取法)

    传送门 NanoApe Loves Sequence Ⅱ Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K ...

随机推荐

  1. 不使用循环语句用if和else实现循环

    如果不使用循环语句,可以使用递归函数来实现循环的效果.递归函数是指在函数内部调用自身的函数.下面是一个使用递归函数来实现循环的示例: (初学者记得写include,这里是个普通函数,所以我没写) de ...

  2. vue-devtools

    今天分享个vue的开发者工具,vue2和vue3都可以使用 先来看个效果: 如何安装? 打开官网vue-devtools 选择你需要版本 安装完记得重启下浏览器,不然是没效果的

  3. 分布式定理--CAP定理

    cap定理指的是,在一个分布式系统中,只能满足cap中的两项. C consistency 一致性 A availability 可用性 P partition tolerance 分区可容错性 -- ...

  4. vue基础使用

    传统dom操作 使用js或jquery库对html页面结构中的指定的区域输出数据 使用vue实现 在html页面中使用好vue需要完成如下步骤即可 引入vue.js文件 定义给vue.js管理的dom ...

  5. 红米K70E支付宝无指纹支付选项的解决方法

    红米K70E这台手机,支付宝里无指纹支付选项,百度了一下,也没结果.自己摸索了下,终于折腾出了指纹支付. 解决方法: 在手机-设置-指纹.面部与密码-指纹解锁-指纹支付-支付宝-更新证书. 杀掉支付宝 ...

  6. .net6 .net core web api json 遇到 400 错误

    环境: .net6 webapi 服务端模型声明 public class TongYiMinPgPayReq { public string mch_no { get; set; } public ...

  7. Kubernetes监控手册03-宿主监控实操

    生产环境大都是在 Linux 下的,所以这篇文章我们先来分享如何使用 Categraf 采集 Linux OS 相关的指标.读完本篇内容,你应该可以完成机器层面的监控了. 原理概述 Categraf ...

  8. linux下后台运行程序

    文章目录 背景 nohup命令 setsid命令 pm2 背景 后台运行程序的时候,如果退出当前的终端(session),你运行的所有程序(包括后台程序),都将被关闭. 原因是:你运行的程序都是你的终 ...

  9. 加速鸿蒙生态共建,蚂蚁mPaaS助力鸿蒙原生应用开发创新

    6月21日-23日,2024华为开发者大会(HDC 2024)如期举行.在22日的[鸿蒙生态伙伴SDK]分论坛中,正式发布了[鸿蒙生态伙伴SDK市场],其中蚂蚁数科旗下移动开发平台mPaaS(以下简称 ...

  10. [AGC030C] Coloring Torus

    非常巧妙的一道构造题,发现对于所构造的 \(n\) 有上限,那么对于 \(K<=500\) 的情况,很好构造,每行全是一个数就行了,对于 \(K>500\) 的情况,显然每行都是 \(1, ...