GSS2—去重最大子段和
GSS2
题意:给定序列
询问次数与序列长度在1e5
级别。
分析
超级神题。
在线算法,发现维护去重似乎非常困难,考虑将序列离线下来。有了这个离线的条件,由于没有修改操作,我们就可以考虑对询问顺序开始魔改处理了。
1e5
常见的做法无非三种可能:
然后再来考虑,如果是线段树该怎么做。利用离线操作,一个套路是:
对询问的某端点进行排序,高效维护某个端点单向移动且支持查询以这个移动端点为端点的区间的答案。
加上去重的特殊性,我们考虑将所有的询问按
维护一个答案序列(也许可以理解为贡献序列)
考虑移动指针
所以我们将
再来考虑如何求解最大子段和问题。
首先,因为此时的
受这种思想的启发,回溯一个阶段,当
但,显然这个做法有优化的空间,我们明显可以保存
这样我们就得到了一个做法:
- 读入询问,离线,统计
- 建立线段树,维护区间最大值,区间历史最大值
- 将询问按右端点排序,建立指针
并不断右移,每次插入就在 上加上 。每次查询就查找 的历史最大值。
至此,我们得到了一个
#define N 100050
#define ll long long
int n,m,a[N],pre[N],b[N];
ll ans[N];
struct node{
int l,r;ll mx,hmx,lz,hlz;
}t[N<<2];
struct Ask{
int l,r,id;
bool operator<(const Ask b){
return r==b.r?l<b.l:r<b.r;
}
}ask[N];
#define lc x<<1
#define rc x<<1|1
void read(int &x){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
x=s*w;
}
void pushup(int x){
t[x].mx=max(t[lc].mx,t[rc].mx);
t[x].hmx=max(t[x].hmx,t[x].mx);
}
void pushdown(node &a,node &b,node &c){
b.hmx=max(b.hmx,b.mx+a.hlz);
c.hmx=max(c.hmx,c.mx+a.hlz);
b.hlz=max(b.hlz,b.lz+a.hlz);
c.hlz=max(c.hlz,c.lz+a.hlz);
b.mx+=a.lz;
c.mx+=a.lz;
b.lz+=a.lz;
c.lz+=a.lz;
a.lz=a.hlz=0;
}
void pushdown(int x){
pushdown(t[x],t[lc],t[rc]);
}
void build(int l,int r,int x){
t[x]={l,r,0,0,0,0};
if(l==r)return ;
int mid=l+r>>1;
build(l,mid,lc);
build(mid+1,r,rc);
}
ll find(int l,int r,int x){
if(l<=t[x].l&&t[x].r<=r){
return t[x].hmx;
}
pushdown(x);
ll ans=0ll;
int mid=t[x].l+t[x].r>>1;
if(l<=mid)ans=max(find(l,r,lc),ans);
if(mid<r)ans=max(find(l,r,rc),ans);
pushup(x);
return ans;
}
void change(int l,int r,ll k,int x){
if(l<=t[x].l&&t[x].r<=r){
t[x].lz+=k;
t[x].hlz=max(t[x].hlz,t[x].lz);
t[x].mx+=k;
t[x].hmx=max(t[x].hmx,t[x].mx);
return ;
}
pushdown(x);
int mid=t[x].l+t[x].r>>1;
if(l<=mid)change(l,r,k,lc);
if(mid<r)change(l,r,k,rc);
pushup(x);
}
void init(){
read(n);
build(1,n,1);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)pre[i]=b[a[i]],b[a[i]]=i;
read(m);
for(int i=1;i<=m;i++)read(ask[i].l),read(ask[i].r),ask[i].id=i;
sort(ask+1,ask+m+1);
}
void solve(){
int l=1;
for(int r=1;r<=n;++r){
change(pre[r]+1,r,a[r],1);
while(ask[l].r==r&&l<=m){
ans[ask[l].id]=find(ask[l].l,ask[l].r,1);
l++;
}
}
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
init();
solve();
for(int i=1;i<=m;i++){
printf("%lld\n",ans[i]);
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战