您的位置:首页 > 产品设计 > UI/UE

SPOJ 1557 Can you answer these queries II(离线处理+线段树求历史最大)

2017-07-19 16:05 525 查看

GSS2 - Can you answer these queries II

#tree

Being a completist and a simplist, kid Yang Zhe cannot solve but get Wrong Answer from most of the OI problems. And he refuse to write two program of same kind at all. So he always failes in contests.

When having a contest, Yang Zhe looks at the score of every problems first. For the problems of the same score, Yang Zhe will do only one of them. If he's lucky enough, he can get all the scores wanted.

Amber is going to hold a contest in SPOJ. She has made a list of N candidate problems, which fit Yang Zhe very well. So Yang Zhe can solve any problem he want. Amber lined up the problems, began to select. She will select a subsequence of the list
as the final problems. Being A girl of great compassion, she'd like to select such a subsequence (can be empty) that Yang Zhe will get the maximal score over all the possible subsequences.

Amber found the subsequence easily after a few minutes. To make things harder, Amber decided that, Yang Zhe can take this contest only if Yang Zhe can answer her
Q questions. The question is: if the final problems are limited to be a subsequence of
list[X..Y] (1 <= X <= Y <= N), what's the maximal possible score Yang Zhe can get?

As we know, Yang Zhe is a bit idiot (so why did he solve the problem with a negative score?), he got Wrong Answer again... Tell him the correct answer!

Input

Line 1: integer N (1 <= N <= 100000);
Line 2: N integers denoting the score of each problem, each of them is a integer in range [-100000, 100000];

Line 3: integer Q (1 <= Q <= 100000);
Line 3+i (1 <= i <= Q): two integers X and
Y denoting the ith question.

Output

Line i: a single integer, the answer to the ith question.

Example

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

Output:
4
5
3


        膜拜杨哲大犇orz……

        Can you answer these queries……这也是一个系列,线段树系列。

        之前第一个我们已经写过了,就是求区间最大连续子串。这个与第一个不同,这个要求是已经取过的数字,第二次遇到了不计算,即子串1、2、1的和是3而不是4。也就意味着,你询问区间的不同,对应的最大连续子串也会不同,故显然在线线段树是做不了的,继续用离线大法。

        为什么这么做,我也不好解释……先对询问按照右端点排序。然后对于第i个数字a[i],我们在线段树区间[1,i]更新使其加上a[i]。如此做了之后我们发现,第一个位置c[1]=a[1]+a[2]+...+a
、第二个位置c[2]=a[2]+a[3]+...+a
、第i个位置c[i]=a[i]+a[i+1]+...+a
。那么对于以n为右端点的询问,它的解就是区间从左端点到n的区域内c的历史最大值。那么,我们就可一个一个的把a[i]加入线段树,在把a[i]加入线段树之后,就可以求出以i为右端点的询问。以此类推,求出所有解。

        但是刚刚说的时候忽略了一个问题,怎么解决重复的数字呢?其实很简单,记录一个p[i],表示数字i上一次出现的位置。那么我每次更新的时候不是从1开始更新,而是从p[i]开始更新,这样就可以保证不会重复加。同时又因为有负数,所以要显然数字先加一个数变成正数。

        现在提出一个疑问,我还疑惑了一会儿。为什么答案是对应区间左端点的历史最大,而是区间的历史最大,左端点处的值c[l]=c[l]+c[l+1]+...+c[r],应该求这个历史最大就行了呀。实际上这样是错误的。因为左端点处的c[l]的初始值是a[l],这个是一定存在的,即他是从左端点开始加的,实际的区间最大子串可以不从左端点开始。

        所以说,线段树等于说称为了求区间历史最大的工具。看到这个会不会似曾相识……嗯,WHU选拔赛那题,是求区间历史最小,永远不会忘记当时死都写不出来……看了当时赛后写的题解,分块的我不说,线段树的那个,写的是什么呀……还说什么更新了一边要把另外一边的lazy下放……其实只是写法整个都不对,push_down操作完全误解了……无法理解当时怎么会犯这种错误。多的我就不说了,具体见代码:

#include<bits/stdc++.h>
#define N 101000
using namespace std;

struct ST
{
struct node
{
int nowmax,hismax,lazy1,lazy2;
int l,r;
} tree[N<<2];

inline void push_up(int i)
{
tree[i].nowmax=max(tree[i<<1].nowmax,tree[i<<1|1].nowmax);
tree[i].hismax=max(tree[i].hismax,tree[i].nowmax);
}

inline void build(int i,int l,int r)
{
tree[i].l=l;
tree[i].r=r;
if (l==r)
{
tree[i].nowmax=tree[i].hismax=0;
tree[i].lazy1=tree[i].lazy2=0;
return;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
push_up(i);
}

inline void push_down(int i)
{
if (!tree[i].lazy1 && !tree[i].lazy2) return;
tree[i<<1].lazy2=max(tree[i<<1].lazy2,tree[i<<1].lazy1+tree[i].lazy2);
tree[i<<1].hismax=max(tree[i<<1].hismax,tree[i<<1].nowmax+tree[i].lazy2);
tree[i<<1].nowmax+=tree[i].lazy1; tree[i<<1].lazy1+=tree[i].lazy1;

tree[i<<1|1].lazy2=max(tree[i<<1|1].lazy2,tree[i<<1|1].lazy1+tree[i].lazy2);
tree[i<<1|1].hismax=max(tree[i<<1|1].hismax,tree[i<<1|1].nowmax+tree[i].lazy2);
tree[i<<1|1].nowmax+=tree[i].lazy1; tree[i<<1|1].lazy1+=tree[i].lazy1;

tree[i].lazy1=tree[i].lazy2=0;
}

inline void update(int i,int l,int r,int delta)
{
if (tree[i].r==r && tree[i].l==l)
{
tree[i].lazy1+=delta;
tree[i].nowmax+=delta;
tree[i].lazy2=max(tree[i].lazy2,tree[i].lazy1);
tree[i].hismax=max(tree[i].hismax,tree[i].nowmax);
return;
}
push_down(i);
int mid=(tree[i].l+tree[i].r)>>1;
if (r<=mid) update(i<<1,l,r,delta);
else if (l>mid) update(i<<1|1,l,r,delta);
else
{
update(i<<1,l,mid,delta);
update(i<<1|1,mid+1,r,delta);
}
push_up(i);
}

inline long long getmax(int i,int l,int r)
{
if (tree[i].l==l && tree[i].r==r) return tree[i].hismax;
push_down(i);
int mid=(tree[i].l+tree[i].r)>>1;
if (r<=mid) return getmax(i<<1,l,r);
else if (l>mid) return getmax(i<<1|1,l,r);
else return max(getmax(i<<1,l,mid),getmax(i<<1|1,mid+1,r));
}

} seg;

struct query
{
int l,r,id;
} q
;

int n,m,p[2*N],a
,ans
;

bool cmp(query a,query b)
{
return a.r<b.r;
}

int main()
{
while(~scanf("%d",&n))
{
seg.build(1,1,n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
int i,j;
sort(q+1,q+1+m,cmp); //询问按照右端点排序
memset(p,0,sizeof(p));
for(i=1,j=1;i<=n;i++)
{
seg.update(1,p[a[i]+N]+1,i,a[i]); //把负数变成正数,从上一个出现的位置+1开始更新
p[a[i]+N]=i; //记录当前位置
for(;q[j].r==i&&j<=m;j++)
ans[q[j].id]=seg.getmax(1,q[j].l,q[j].r);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: