Gym - 102028H Can You Solve the Harder Problem? (后缀数组+RMQ+单调栈)
题意:求一个序列中本质不同的连续子序列的最大值之和。
由于要求“本质不同”,所以后缀数组就派上用场了,可以从小到大枚举每个后缀,对于每个sa[i],从sa[i]+ht[i]开始枚举(ht[0]=0),这样就能不重复不遗漏地枚举出每一个子串。
但是这样做,最坏情况仍旧是$O(n^2)$的,可能会被卡掉,需要进一步优化。
对于每个sa[i],设k=sa[i]+ht[i],则问题转化成了求max(s[sa[i]],s[sa[i]+1],...,s[k])+max(s[sa[i]],s[sa[i]+1],...,s[k+1])+...+max(s[sa[i]],s[sa[i]+1],...,s[n-1])。
可以发现,随着下标的增大,最大值是单调不减的,这启示我们利用单调栈将后缀进行分段,对于每个最大值不同的段求出后缀和,对于每个sa[i],利用RMQ求出s[sa[i],k]中的最大值mx,然后在单调栈上二分找到第一个大于mx的值的下标,将贡献分成左右两部分相加即可。
一开始可以将初始序列离散化,求出后缀数组后再还原回去,这样复杂度就不依赖于序列元素的大小而只与序列长度有关了,$O(nlogn)$
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+,inf=0x3f3f3f3f;
int s[N],buf1[N],buf2[N],b[N],nb,c[N],n,sa[N],ht[N],rnk[N],ST[N][],Log[N],sta[N],idx[N],tp;
ll sum[N];
void Sort(int* x,int* y,int m) {
for(int i=; i<m; ++i)c[i]=;
for(int i=; i<n; ++i)++c[x[i]];
for(int i=; i<m; ++i)c[i]+=c[i-];
for(int i=n-; i>=; --i)sa[--c[x[y[i]]]]=y[i];
}
void da(int* s,int n,int m=) {
int *x=buf1,*y=buf2;
x[n]=y[n]=-;
for(int i=; i<n; ++i)x[i]=s[i],y[i]=i;
Sort(x,y,m);
for(int k=; k<n; k<<=) {
int p=;
for(int i=n-k; i<n; ++i)y[p++]=i;
for(int i=; i<n; ++i)if(sa[i]>=k)y[p++]=sa[i]-k;
Sort(x,y,m),p=,y[sa[]]=;
for(int i=; i<n; ++i)y[sa[i]]=x[sa[i-]]==x[sa[i]]&&x[sa[i-]+k]==x[sa[i]+k]?p-:p++;
if(p==n)break;
swap(x,y),m=p;
}
}
void getht() {
for(int i=; i<n; ++i)rnk[sa[i]]=i;
ht[]=,s[n]=-;
for(int i=,k=; i<n; ++i) {
if(k)--k;
if(!rnk[i])continue;
for(; s[i+k]==s[sa[rnk[i]-]+k]; ++k);
ht[rnk[i]]=k;
}
}
void build() {
for(int i=; i<n; ++i)ST[i][]=s[i];
for(int k=; k<=Log[n]; ++k)
for(int i=; i+(<<k)-<n; ++i)
ST[i][k]=max(ST[i][k-],ST[i+(<<(k-))][k-]);
}
int qry(int L,int R) {
int k=Log[R-L+];
return max(ST[L][k],ST[R-(<<k)+][k]);
}
struct QR {
int k,mx;
bool operator<(const QR& b)const {return k<b.k;}
} qr[N];
void push(int x,int i) {
for(; ~tp&&x>=sta[tp]; --tp);
sta[++tp]=x,sum[tp]=(ll)x*(idx[tp-]-i)+sum[tp-],idx[tp]=i;
}
int main() {
Log[]=-;
for(int i=; i<N; ++i)Log[i]=Log[i>>]+;
int T;
for(scanf("%d",&T); T--;) {
scanf("%d",&n);
for(int i=; i<n; ++i)scanf("%d",&s[i]);
nb=;
for(int i=; i<n; ++i)b[nb++]=s[i];
sort(b,b+nb),nb=unique(b,b+nb)-b;
for(int i=; i<n; ++i)s[i]=lower_bound(b,b+nb,s[i])-b;
da(s,n,n+),getht();
for(int i=; i<n; ++i)s[i]=b[s[i]];
build();
ll ans=;
for(int i=; i<n; ++i) {
int k=sa[i]+ht[i];
qr[i]= {k,qry(sa[i],k)};
}
sort(qr,qr+n);
sta[tp=]=inf,sum[tp]=,idx[tp]=n;
for(int i=n-,j=n-; i>=; --i) {
int k=qr[i].k,mx=qr[i].mx;
for(; j>=k; --j)push(s[j],j);
int t=lower_bound(sta,sta+tp+,mx,greater<int>())-sta-;
ans+=(ll)mx*(idx[t]-k)+sum[t];
}
printf("%lld\n",ans);
}
return ;
}
Gym - 102028H Can You Solve the Harder Problem? (后缀数组+RMQ+单调栈)的更多相关文章
- HDU - 5008 Boring String Problem (后缀数组+二分法+RMQ)
Problem Description In this problem, you are given a string s and q queries. For each query, you sho ...
- HDU 5008 Boring String Problem(后缀数组+二分)
题目链接 思路 想到了,但是木写对啊....代码 各种bug,写的乱死了.... 输出最靠前的,比较折腾... #include <cstdio> #include <cstring ...
- HDU5008 Boring String Problem(后缀数组)
练习一下字符串,做一下这道题. 首先是关于一个字符串有多少不同子串的问题,串由小到大排起序来应该是按照sa[i]的顺序排出来的产生的. 好像abbacd,排序出来的后缀是这样的 1---abbacd ...
- HDU1086You can Solve a Geometry Problem too(判断线段相交)
You can Solve a Geometry Problem too Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/3 ...
- hdu 1086 You can Solve a Geometry Problem too
You can Solve a Geometry Problem too Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/3 ...
- DFS+剪枝 HDOJ 5323 Solve this interesting problem
题目传送门 /* 题意:告诉一个区间[L,R],问根节点的n是多少 DFS+剪枝:父亲节点有四种情况:[l, r + len],[l, r + len - 1],[l - len, r],[l - l ...
- hdu5323 Solve this interesting problem(爆搜)
转载请注明出处: http://www.cnblogs.com/fraud/ ——by fraud Solve this interesting problem Time Limit ...
- (hdu step 7.1.2)You can Solve a Geometry Problem too(乞讨n条线段,相交两者之间的段数)
称号: You can Solve a Geometry Problem too Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/ ...
- HDU 1086:You can Solve a Geometry Problem too
pid=1086">You can Solve a Geometry Problem too Time Limit: 2000/1000 MS (Java/Others) Mem ...
随机推荐
- JavaScript 检测值
了解常见的真值和假值,可以增强判断能力.在使用if判断时,提升编码速度. 了解常见的检测和存在,一样可以增强判断能力,而且是必须掌握的. 数组和对象被视为真值 1 2 3 4 5 6 7 8 9 10 ...
- PCM-FTL
1. 运行时第一行需要是write 错误位置 trace_stat->sectors += length;
- tensorflow lite 之生成 tflite 模型文件
下载最新的的tensorflow源码. 1.配置 tflite 文件转换所需环境 安装 bazel 编译工具 https://docs.bazel.build/versions/master/inst ...
- Go-函数高级使用-条件分支-包管理-for循环-switch语句-数组及切片-与或非逻辑符
目录 科普 python 注释 # 函数高级 if else if else 包管理 下载第三方包 比较热门的框架 for 循环 for 循环的几种写法 switch 语句 数组及数组切片 数组迭代 ...
- 第十二章 ZYNQ-MIZ702 PS读写PL端BRAM
本篇文章目的是使用Block Memory进行PS和PL的数据交互或者数据共享,通过zynq PS端的Master GP0端口向BRAM写数据,然后再通过PS端的Mater GP1把数据读出来,将 ...
- configure,make和make install关系
linux编译安装中configure.make和make install各自的作用 ./configure是用来检测你的安装平台的目标特征的.configure根据给定的参数和系统环境会生成Make ...
- 怎样理解document节点
1. document是七种文档节点中的一种, 是最顶级的一种节点; 2. 其他六种节点都包在document节点之内; 3. document既是一种节点的名字, 也是这种节点在DOM中的实例对象; ...
- [http]HTTP状态码含义
HTTP状态码 当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求.当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应 ...
- 作业12:List集合类
一 为什么使用List? 1 List与数组 List 数组 可变长度 固定长度 方便的接口(支持Java8的Stream) / 2 List的实现方式 数组:ArrayList 查询效率比较高 插入 ...
- 关于Visual Studio 2019安装时共享组件、工具和 SDK安装位置不能更改的问题
解决办法: 更改注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\Setup下的SharedInstallationPath项为所要的路径