SP1557 GSS2 - Can you answer these queries II
作者:互联网
SP1557 GSS2 - Can you answer these queries II
题目大意
给出 \(n\) 个数,\(q\) 次询问,求最大子段和,相同的数只算一次。
分析
看到一个区间内相同的数只能算一次,经验告诉我们要考虑离线。
我们将区间按照右端点排序,用pre[i]
来表示i
上次出现的位置。
接下来,我们来考虑线段树需要维护什么。
首先,我们考虑之前不加条件时,我们维护的值还可以维护嘛?
很显然,不太可行,因为当一个区间出现多个值的时候,我们不知道需要哪个位置的值,而其他的不要。
我们考虑从离线角度思考,我们维护四个值sum
,stag
,hismax
,htag
。假设此时是以y
为结尾的区间。
sum[i]
表示从[i,y]
的所有值只算一次的和stag
表示区间加懒标记hismax[i]
表示以i
为起点,终点小于t
的所有区间的最大值htag
历史加懒标记,当多个区间加操作发生时,我们只加其中最大的。
那具体怎么加,才能使得所有值值算一次的和呢?
还记得,我们有一个pre[i]
嘛,此时我们直接从[pre[i]+1,i]
区间加上w[i]
。
这样我们能保证,相同的值,不会多次对sum
造成影响。
这个思路非常巧妙,我们再来顺一遍。
我们,想要保证一个区间内相同的值只被算一次,同时加上离线。
我们考虑维护sum
,同时每次碰到一个节点i
,我们区间加[pre[i]+1,i]
,这样可以保证该值的影响只影响到前面从上一次遇到相同的值的位置的下一个到现在的所有位置。这个消除多个影响的设计在于,如果我们直接维护和,这样就可以通过限定区域来限定影响。同时这个和的设计也很巧妙。其利用了离线的状态去设计了和的状态定义。
具体的一些维护细节,可以直接看代码。
Ac_code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
struct Node
{
int l,r;
LL sum,stag,hismax,htag;
}tr[N<<2];
struct Line
{
int l,r,id;
bool operator<(const Line& W)const
{
return r<W.r;
}
}line[N];
int w[N],pos[2*N+10];
LL ans[N];
int n,m;
void push(Node &u,Node &l,Node &r)
{
u.sum = max(l.sum,r.sum);
u.hismax = max(l.hismax,r.hismax);
}
void pushup(int u)
{
push(tr[u],tr[u<<1],tr[u<<1|1]);
}
void pushdown(int u)
{
auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
left.hismax = max(left.hismax,left.sum+root.htag);//每次只加加序列的最大值
left.sum += root.stag;
left.htag = max(left.htag,left.stag+root.htag);//维护累积加序列的最大值
left.stag += root.stag;
right.hismax = max(right.hismax,right.sum+root.htag);
right.sum += root.stag;
right.htag = max(right.htag,right.stag+root.htag);
right.stag += root.stag;
root.stag = root.htag = 0;
}
void build(int u,int l,int r)
{
tr[u] = {l,r};
if(l==r) return ;
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void modify(int u,int l,int r,int k)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].sum += k;
tr[u].hismax = max(tr[u].hismax,tr[u].sum);
tr[u].stag += k;
tr[u].htag = max(tr[u].htag,tr[u].stag);
return ;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
Node query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u];
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(r<=mid) return query(u<<1,l,r);
else if(l>mid) return query(u<<1|1,l,r);
else
{
auto left = query(u<<1,l,r);
auto right = query(u<<1|1,l,r);
Node res;
push(res,left,right);
return res;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",w+i);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int l,r;scanf("%d%d",&l,&r);
line[i] = {l,r,i};
}
sort(line+1,line+1+m);
build(1,1,n);
int idx = 1;
for(int i=1;i<=m;i++)
{
for(int j=idx;j<=line[i].r;j++)
{
modify(1,pos[w[j]+N]+1,j,w[j]);
pos[w[j]+N] = j;
}
ans[line[i].id] = query(1,line[i].l,line[i].r).hismax;
idx = line[i].r + 1;
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
标签:pre,these,sum,离线,SP1557,II,区间,维护,我们 来源: https://www.cnblogs.com/aitejiu/p/16641967.html