BZOJ2482: [Spoj1557] Can you answer these queries II

时间:2023-03-08 20:49:40

题解:

从没见过这么XXX的线段树啊。。。

T_T

我们考虑离线做,按1-n一个一个插入,并且维护区间【 j,i】(i为当前插入的数)j<i的最优值。

但这个最优值!!!

我们要保存历史的最优值,以及当前的最优值!!!还有lazy!!!也得分历史和现在!!T_T

怎么搞!!!

inline void update(int k,int z1,int z2)
{
t[k].tag[]=max(t[k].tag[],t[k].tag[]+z2);
t[k].tag[]+=z1;
t[k].mx[]=max(t[k].mx[],t[k].mx[]+z2);
t[k].mx[]+=z1;
t[k].mx[]=max(t[k].mx[],t[k].mx[]);
}
inline void pushdown(int k)
{
if(!t[k].tag[]&&!t[k].tag[])return;
update(k<<,t[k].tag[],t[k].tag[]);
update(k<<|,t[k].tag[],t[k].tag[]);
t[k].tag[]=t[k].tag[]=;
}

就是这么鬼畜,然后剩下的套模板就好了。。。

其实也蛮好理解的。

tag[0]表示从上次pushdown到现在加了多少

tag[1]表示从上次pushdown到现在tag[0]最多达到过多少,因为祖先的操作可能累计了很多次,我们只需要向下传一下最大的。

mx[0]和mx[1]与tag类似。

对拍了一组数据发现RE了,调了1h发现我dtmk写错了!!!说多了都是泪啊T_T

不过#2还是蛮开心的 哈哈~

代码:

 #include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<string>
#define inf 1000000000
#define maxn 200000+5
#define maxm 100000+5
#define eps 1e-10
#define ll long long
#define pa pair<int,int>
#define for0(i,n) for(int i=0;i<=(n);i++)
#define for1(i,n) for(int i=1;i<=(n);i++)
#define for2(i,x,y) for(int i=(x);i<=(y);i++)
#define for3(i,x,y) for(int i=(x);i>=(y);i--)
#define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go)
#define mod 1000000007
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=*x+ch-'';ch=getchar();}
return x*f;
}
int n,m,v[maxn],last[maxn],ans[maxn];
struct rec{int l,r,id;}a[maxn];
inline bool cmp(rec x,rec y){return x.r<y.r;}
struct seg{int l,r,mx[],tag[];}t[*maxn];
inline void build(int k,int l,int r)
{
t[k].l=l;t[k].r=r;int mid=(l+r)>>;
if(l==r)return;
build(k<<,l,mid);build(k<<|,mid+,r);
}
inline void update(int k,int z1,int z2)
{
t[k].tag[]=max(t[k].tag[],t[k].tag[]+z2);
t[k].tag[]+=z1;
t[k].mx[]=max(t[k].mx[],t[k].mx[]+z2);
t[k].mx[]+=z1;
t[k].mx[]=max(t[k].mx[],t[k].mx[]);
}
inline void pushup(int k)
{
for0(i,)t[k].mx[i]=max(t[k<<].mx[i],t[k<<|].mx[i]);
}
inline void pushdown(int k)
{
if(!t[k].tag[]&&!t[k].tag[])return;
update(k<<,t[k].tag[],t[k].tag[]);
update(k<<|,t[k].tag[],t[k].tag[]);
t[k].tag[]=t[k].tag[]=;
}
inline void add(int k,int x,int y,int z)
{
int l=t[k].l,r=t[k].r,mid=(l+r)>>;
if(l==x&&r==y){update(k,z,);return;}
pushdown(k);
if(y<=mid)add(k<<,x,y,z);
else if(x>mid)add(k<<|,x,y,z);
else add(k<<,x,mid,z),add(k<<|,mid+,y,z);
pushup(k);
}
inline int query(int k,int x,int y)
{
int l=t[k].l,r=t[k].r,mid=(l+r)>>;
if(l==x&&r==y)return t[k].mx[];
pushdown(k);
if(y<=mid)return query(k<<,x,y);
else if(x>mid)return query(k<<|,x,y);
else return max(query(k<<,x,mid),query(k<<|,mid+,y));
}
#define last(i) last[100000+i]
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
n=read();
for1(i,n)v[i]=read();
build(,,n);
m=read();
for1(i,m)a[i].l=read(),a[i].r=read(),a[i].id=i;
sort(a+,a+m+,cmp);
int j=;
for1(i,n)
{
int tmp=last(v[i]);last(v[i])=i;
add(,tmp+,i,v[i]);
while(a[j].r==i)ans[a[j].id]=query(,a[j].l,i),j++;
}
for1(i,m)printf("%d\n",ans[i]);
return ;
}

2482: [Spoj1557] Can you answer these queries II

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 115  Solved: 67
[Submit][Status]

Description

给定n个元素的序列。 
给出m个询问:求l[i]~r[i]的最大子段和(可选空子段)。 
这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次。 
比如:1,2,3,2,2,2出现了3次,但只算一次,于是这个序列的和是1+2+3=6。

Input

第一行一个数n。 
第二行n个数,为给定的序列,这些数的绝对值小于等于100000。 
第三行一个数m。 
接下来m行,每行两个数,l[i],r[i]。

Output

M行,每行一个数,为每个询问的答案。



Sample Input

9
4 -2 -2 3 -1 -4 2 2 -6
3
1 2
1 5
4 9

Sample Output

4
5
3

HINT

【数据说明】

30%:1 <= n, m <= 100

100%:1 <= n, m <= 100000

Source