CF1787I Treasure Hunt 题解
题意描述:
定义一个序列的权值为一段前缀与一段子段,满足要么前缀与子段不交,要么完全包含的和的最大值,给定一个序列
,求 的所有子区间的权值和
发现如果前缀与子段有交且不包含,那么一定不优,因为子段未被包含的一段一定是正的,不然就不是最大子段了
因此可以把问题拆成两部分:所有子区间的最大前缀以及所有子区间的最大子段和
第一部分是 trivial 的,直接用单调栈或笛卡尔树求出贡献范围即可
第二部分显然考虑分治,对于跨过中点的区间的贡献,可以分成三种情况(即线段树求最大子段和的三种)
我们设
在从中点向左扫的过程中,首先
,拆成 和 ,移项得 ,因此取到这种情况的分界点 一定单调不降 ,类似上一种, ,随着 的移动,可能的位置会越来越少 ,拆开移项得 ,那么这种情况的分界点是不断向后移动的
于是贡献分成三段,第一段显然是
一个感性的理解:
时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 1000005
#define mid ((l+r)>>1)
using namespace std;
int read(){
int w=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
return w*h;
}
const int mod=998244353;
int n,a[N],dp[N],ddp[N],sum[N],sumdp[N],sumsum[N],summ[N],mus[N];
int Divide(int l,int r){
if(l==r)return max(0ll,a[l]);
for(int i=l;i<=r;i++)dp[i]=ddp[i]=sum[i]=sumdp[i]=sumsum[i]=0;
for(int i=mid+1,su=0;i<=r;i++){
su+=a[i];sum[i]=max((i==mid+1)?0:sum[i-1],su);
dp[i]=(i==mid+1)?a[i]:max(dp[i-1]+a[i],a[i]);
ddp[i]=max((i==mid+1)?0:ddp[i-1],dp[i]);
}
for(int i=mid,su=0;i>=l;i--){
su+=a[i];sum[i]=max((i==mid)?0:sum[i+1],su);
dp[i]=(i==mid)?a[i]:max(dp[i+1]+a[i],a[i]);
ddp[i]=max((i==mid)?0:ddp[i+1],dp[i]);
}
for(int i=mid+1;i<=r;i++){
sumdp[i]=(ddp[i]+(i==mid+1?0:sumdp[i-1]))%mod;
sumsum[i]=(sum[i]+(i==mid+1?0:sumsum[i-1]))%mod;
}
int cur1=mid+1,cur2=mid+1,ans=0;
for(int i=mid;i>=l;i--){
while(cur2<=r&&ddp[cur2]<max(sum[i]+sum[cur2],ddp[i]))cur2++;
while(cur1<cur2&&sum[i]+sum[cur1]<max(ddp[i],ddp[cur1]))cur1++;
(ans+=ddp[i]*(cur1-1-mid)%mod)%=mod;
(ans+=sum[i]*(cur2-cur1)%mod+(sumsum[cur2-1]-sumsum[cur1-1])%mod+mod)%=mod;
(ans+=sumdp[r]-sumdp[cur2-1]+mod)%=mod;
}
return (ans+Divide(l,mid)+Divide(mid+1,r))%mod;
}
namespace Sparse_Table{
int Max[20][N],lg[N];
int chkmax(int x,int y){return summ[x]>summ[y]?x:y;}
void Build(){
for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++)Max[0][i]=i;
for(int j=1;j<=lg[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++)
Max[j][i]=chkmax(Max[j-1][i],Max[j-1][i+(1<<(j-1))]);
}
int Ask(int l,int r){
if(l>r)swap(l,r);int k=lg[r-l+1];
return chkmax(Max[k][l],Max[k][r-(1<<k)+1]);
}
}
using namespace Sparse_Table;
int Prefix(int l,int r){
if(l>r)return 0;int Mid=Ask(l,r);
int res=(summ[Mid]*(Mid-l)%mod-(mus[Mid]-mus[l])%mod+mod+max(0ll,summ[Mid]-summ[l-1]))*(r-Mid+1)%mod;
return (res+Prefix(l,Mid-1)+Prefix(Mid+1,r))%mod;
}
void solve(){
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)summ[i]=summ[i-1]+a[i],mus[i]=mus[i-1]+summ[i-1];
Build();
printf("%lld\n",(Divide(1,n)+Prefix(1,n))%mod);
}
signed main(){
int T=read();
while(T--)solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统