您的位置:首页 > 其它

Live archive 3938 "Ray,Pass me the Dishes"

2016-08-24 20:51 381 查看
题意:给出一个长度为n的整数序列D,你的任务是对m个询问做出回答,对于询问(a,b),需要找到两个下标x,y使得a<=x<=y<=b并且使得Dx+Dx+1+……+Dy尽量大,如果有多组满足条件,应使x尽量小,如果还是有多解,y应该尽量小

这里要用线段树分治,一个连续和要么被包含在左儿子或者右儿子,要么跨越两个儿子,对于一个结点维护最大前缀和,后缀和,最大连续和。

题目要求输出区间,所以还要保存连续和最大的区间,以及前缀和,后缀和的位置。为了维护最大前缀和以及后缀和还需要一个区间和。

所以这道题写起来非常麻烦,但是思路还是 很清晰的

#include<cstdio>
#include<iostream>
#define ls (r<<1)
#define rs ((r<<1)+1)
#define LL long long
using namespace std;
const int maxn=5e5+5;
struct wk{
LL pre,suf,sub,sum;
int l,r,pr,sl;
}tree[maxn<<2];
int n,m,s[maxn];
int x,y;
inline void _read(int &x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9')
{if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
void updata(wk&u,wk&s1,wk&s2){
if(s1.pre>=s1.sum+s2.pre)u.pr=s1.pr,u.pre=s1.pre;
else u.pr=s2.pr,u.pre=s1.sum+s2.pre;
if(s2.suf<=s2.sum+s1.suf)u.sl=s1.sl,u.suf=s2.sum+s1.suf;
else u.sl=s2.sl,u.suf=s2.suf;
if(s1.sub>=s2.sub)u.l=s1.l,u.r=s1.r,u.sub=s1.sub;
else u.l=s2.l,u.r=s2.r,u.sub=s2.sub;
if(u.sub<s1.suf+s2.pre||(u.sub==s1.suf+s2.pre&&(u.l>s1.sl||(u.l==s1.sl&&u.r>s2.pr))))
u.sub=s1.suf+s2.pre,u.l=s1.sl,u.r=s2.pr;
u.sum=s1.sum+s2.sum;
}
void build(int r,int L,int R){
if(L==R){
wk& u=tree[r];
u.pre=u.suf=u.sub=u.sum=s[L];
u.l=u.r=u.pr=u.sl=L;
return;
}
int mid=(L+R)>>1;
build(ls,L,mid);
build(rs,mid+1,R);
updata(tree[r],tree[ls],tree[rs]);
}
wk find(int r,int L,int R){
if(x<=L&&R<=y)return tree[r];
int mid=(L+R)>>1;
wk ret;
if(x<=mid&&mid<y){
wk a=find(ls,L,mid);
wk b=find(rs,mid+1,R);
updata(ret,a,b);
return ret;
}
if(y<=mid)
return find(ls,L,mid);
return find(rs,mid+1,R);
}
int main(){
int kase=0;
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++)_read(s[i]);
build(1,1,n);
printf("Case %d:\n",++kase);
while(m--){
_read(x);_read(y);
wk ans=find(1,1,n);
printf("%d %d\n",ans.l,ans.r);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: