AtCoder Beginner Contest 248 H
https://atcoder.jp/contests/abc248/tasks/abc248_h
官方题解使用的是线段树,不过分治可能更简单一些。
我们调用,表示区间的合法子序列个数。
根据分治的套路:,其中表示跨越左右两个侧的合法子序列个数。(这里和下面“左侧”指的是区间,“右侧”指的是区间)
问题是如何求。
分为下面四种情况:
- 最大、最小值都在左侧
- 最大、最小值都在右侧
- 最大值在左侧,最小值在右侧
- 最大值在右侧,最小值在左侧
1,2两种情况类似,我们就讨论情况1,另一种程序是差不多的。
若最大、最小值都在左侧,那么我们枚举,表示合法区间的左端点,枚举,表示当前的大小,那么:,可以变形成:。而最小、最大值都在左侧,故我们可以知道和。接下来我们要判断是否合法。首先,其次,,否则最大或最小值就不在左侧了。
预处理,时间复杂度为。
3,4两种情况,我们只讨论4,另一种程序是差不多的。
我们继续枚举,意义同上,此时必须满足:
此时,我们发现,若从大往小枚举(即从枚举到),那么越来越小,越来越大。那么我们可以使用单调队列维护合法的的位置。
那么如何进行统计呢?我们也是枚举,此时都是已知的,而是未知的,那么:
故我们维护一个桶,单调队列加入一个元素时,,删除时,查询时只要查的大小即可。
注意到有可能小于,故我们要将所有下标加上。
时间复杂度也是。
故,一次分治的复杂度为,一共层,总的时间复杂度为。
代码如下:
#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
using ll=long long;
const int maxn=150005;
int n,k;
int a[maxn],mx[maxn],mn[maxn],cnt[maxn+maxn];
ll divide(int l,int r) {
if(l==r) return 1ll;
int m=l+r>>1; ll ret=divide(l,m)+divide(m+1,r);
mx[m]=mn[m]=a[m],mx[m+1]=mn[m+1]=a[m+1];
for(int i=m-1;i>=l;i--) {
mx[i]=std::max(mx[i+1],a[i]);
mn[i]=std::min(mn[i+1],a[i]);
}
for(int i=m+2;i<=r;i++) {
mx[i]=std::max(mx[i-1],a[i]);
mn[i]=std::min(mn[i-1],a[i]);
}
for(int L=m;L>=l;L--)
for(int K=0;K<=k;K++) {
int R=mx[L]-mn[L]+L-K;
if(m<R&&R<=r&&mx[R]<mx[L]&&mn[R]>mn[L]) ret++;//注意判断R的条件不能漏
}
for(int R=m+1;R<=r;R++)
for(int K=0;K<=k;K++) {
int L=R+K-mx[R]+mn[R];
if(l<=L&&L<=m&&mx[L]<mx[R]&&mn[L]>mn[R]) ret++;
}
int R1=m+1,R2=m+1;
for(int L=m;L>=l;L--) {
while(R2<=r&&mn[R2]>mn[L]) cnt[mx[R2]-R2+n]++,R2++;//注意:单调队列必须先加后删
while(R1<R2&&mx[R1]<mx[L]) cnt[mx[R1]-R1+n]--,R1++;
for(int K=0;K<=k;K++) ret+=cnt[mn[L]-L+K+n];
}
while(R1<R2) cnt[mx[R1]-R1+n]--,R1++;//清空cnt
int L1=m,L2=m;
for(int R=m+1;R<=r;R++) {
while(L2>=l&&mn[L2]>mn[R]) cnt[mx[L2]+L2]++,L2--;
while(L1>L2&&mx[L1]<mx[R]) cnt[mx[L1]+L1]--,L1--;
for(int K=0;K<=k;K++) ret+=cnt[R+K+mn[R]];
}
while(L1>L2) cnt[mx[L1]+L1]--,L1--;
return ret;
}
int main() {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
printf("%lld\n",divide(1,n));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话