After doing Ray a great favor to collect sticks for Ray, Poor Neal becomes very hungry. In return for Neal's help, Ray makes a great dinner for Neal. When it is time for dinner, Ray arranges all the dishes he makes in a single line (actually this line is very long ... , the dishes are represented by 1, 2, 3 ... ). ``You make me work hard and don't pay me! You refuse to teach me Latin Dance! Now it is time for you to serve me", Neal says to himself.
Every dish has its own value represented by an integer whose absolute value is less than 1,000,000,000. Before having dinner, Neal is wondering about the total value of the dishes he will eat. So he raises many questions about the values of dishes he would have.
For each question Neal asks, he will first write down an interval [a, b] (inclusive) to represent all the dishes a, a + 1,..., b , where a and b are positive integers, and then asks Ray which sequence of consecutive dishes in the interval has the most total value. Now Ray needs your help.
Input
The input file contains multiple test cases. For each test case, there are two integers n and m in the first line (n, m < 500000) . n is the number of dishes and m is the number of questions Neal asks.
Then n numbers come in the second line, which are the values of the dishes from left to right. Next m lines are the questions and each line contains two numbers a , b as described above. Proceed to the end of the input file.
Output
For each test case, output m lines. Each line contains two numbers, indicating the beginning position and end position of the sequence. If there are multiple solutions, output the one with the smallest beginning position. If there are still multiple solutions then, just output the one with the smallest end position. Please output the result as in the Sample Output.
Sample Input
3 1
1 2 3
1 1
Sample Output
Case 1:
1 1
---
题意:给出长度为n的整数序列,然后m次询问,对于每次询问,要求找到区间内的两个下标x,y使得区间和尽量大,如果有多解,x尽量小,还有多解,那么y也尽量小
#include<iostream>
#include<set>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define mem(a,b) memset(a,b,sizeof a)
#define lson id<<1
#define rson id<<1|1
const int N = 5e5+7;
const int INF=0x3f3f3f3f;
struct node
{
int l,r,suf_L,pre_R;//要求的答案的左右端点,最大后缀和的左端点,最大前缀和的右端点
ll max_all,max_pre,max_suf,sum;//最大连续和,最大前缀和,最大后缀和,区间值;
}a[N<<1];
ll s[N<<1];
void pushup(int l,int r,int id){
a[id].max_pre=a[lson].max_pre;
a[id].pre_R=a[lson].pre_R;
a[id].max_suf=a[rson].max_suf;
a[id].suf_L=a[rson].suf_L;
a[id].sum=a[lson].sum+a[rson].sum;//sum用于求最大前缀和还有最大后缀和
//左区间sum+右区间 pre
a[id].max_all=a[lson].sum+a[rson].max_pre;
a[id].l=l;
a[id].r=a[rson].pre_R;
//左区间后缀suf+右区间 pre;
if(a[id].max_all<a[rson].max_pre+a[lson].max_suf||(a[id].max_all==a[rson].max_pre+a[lson].max_suf&&a[id].l>a[lson].suf_L)){
a[id].max_all=a[rson].max_pre+a[lson].max_suf;
a[id].l=a[lson].suf_L;
a[id].r=a[rson].pre_R;
}
//左区间的max_all
if(a[id].max_all<a[lson].max_all||(a[id].max_all==a[lson].max_all&&a[id].l==a[lson].l)){
a[id].max_all=a[lson].max_all;
a[id].r=a[lson].r;
a[id].l=a[lson].l;
}
//右区间的max_all;
if(a[id].max_all<a[rson].max_all||(a[id].max_all==a[rson].max_all&&a[id].l>a[rson].suf_L)){
a[id].max_all=a[rson].max_all;
a[id].l=a[rson].l;
a[id].r=a[rson].r;
}
//左区间的后缀suf+右区间的sum;
if(a[id].max_all<a[lson].max_suf+a[rson].sum||(a[id].max_all==a[lson].max_suf+a[rson].sum&&a[id].l>a[lson].suf_L)){
a[id].max_all=a[lson].max_suf+a[rson].sum;
a[id].l=a[lson].suf_L;
a[id].r=r;
}
//修改区间[l,r]的后缀和与前缀和的位置
if(a[id].max_suf<=a[lson].max_suf+a[rson].sum){//这里为什么时<=?因为题目要求要让x越小越好,如果有
a[id].max_suf = a[lson].max_suf+a[rson].sum;//多组解,那么y尽量越小越好;
a[id].suf_L = a[lson].suf_L;//在保证最大后缀的基础上,当然希望后缀的位置越小越好
}
if(a[id].max_pre<a[rson].max_pre+a[lson].sum){
a[id].max_pre = a[rson].max_pre+a[lson].sum;
a[id].pre_R=a[rson].pre_R;//这一行不能在取等号的前提下改变值了,因为前缀和一旦改变
} //就使其前缀的位置变大,也就是大于当前区间的左端点
}
void bulid(int l,int r,int id){
if(l==r){ll temp;
scanf("%lld",&temp);
a[id].max_all=temp;a[id].max_pre=temp;
a[id].max_suf=temp;a[id].sum=temp;
a[id].l=a[id].r=a[id].pre_R=a[id].suf_L=l;
return ;
}
int mid=(l+r)>>1;
bulid(l,mid,lson);
bulid(mid+1,r,rson);
pushup(l,r,id);
}
node qurry(int l,int r,int L,int R,int id){
if(l==L&&r==R) return a[id];//对于取值查询有可能是在线段树的一个区间,也有可能是在两个区间
//比如查询[1,4]就是一个线段树的区间,但是查询[1,5]的时候就需要到两个区间里面取寻找答案
int mid=(L+R)>>1;
if(r<=mid) return qurry(l,r,L,mid,lson);
else if(l>mid) return qurry(l,r,mid+1,R,rson);
else{//下面使求解当答案在两个区间的时候的求解情况
node t1=qurry(l,mid,L,mid,lson);
node t2=qurry(mid+1,r,mid+1,R,rson);
node t;
t.sum = t1.sum + t2.sum;
//这里的对比情况和上面的合并时一种思路;
t.max_pre=t1.max_pre;
t.pre_R=t1.pre_R;
t.max_suf=t2.max_suf;
t.suf_L=t2.suf_L;
//左区间总和+右区间最大前缀
t.max_all=t1.sum+t2.max_pre;
t.l=l;
t.r=t2.pre_R;
//左区间最大连续和
if(t.max_all<t1.max_all||(t.max_all==t1.max_all&&t.l==t1.l)){
t.max_all=t1.max_all;
t.r=t1.r;
t.l=t1.l;
}
//左区间最大后缀和+右区间最大前缀和
if(t.max_all<t1.max_suf+t2.max_pre||(t.max_all==t1.max_suf+t2.max_pre&&t.l>t1.suf_L)){
t.max_all=t1.max_suf+t2.max_pre;
t.l=t1.suf_L;
t.r=t2.pre_R;
}
//左区间最大后缀和+右区间的区间和
if(t.max_all<t1.max_suf+t2.sum||(t.max_all==t1.max_suf+t2.sum&&t.l>t1.suf_L)){
t.max_all=t1.max_suf+t2.sum;
t.l=t1.suf_L;
t.r=t2.r;
}
//右区间的最大连续和
if(t.max_all<t2.max_all || (t.max_all==t2.max_all&&t.l>t2.suf_L)){//这个地方注意
t.max_all=t2.max_all;
t.l=t2.l;
t.r=t2.r;
}
//修改最大前缀与后缀的位置
if(t.max_pre<t1.sum+t2.max_pre){
t.max_pre=t1.sum+t2.max_pre;
t.pre_R=t2.pre_R;
}
if(t.max_suf<=t1.max_suf+t2.sum){
t.max_suf=t1.max_suf+t2.sum;
t.suf_L=t1.suf_L;
}
return t;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
int n,m;
int T=1;
while(scanf("%d%d",&n,&m)!=EOF){
bulid(1,n,1);
printf("Case %d:\n",T++);
while(m--){int l,r;
scanf("%d%d",&l,&r);
node t=qurry(l,r,1,n,1);
printf("%d %d\n",t.l,t.r);
}
}
return 0;
}
下面的代码我改了一下,就是判断最大连续和的地方,仔细体会一下
#include<iostream>
#include<set>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define mem(a,b) memset(a,b,sizeof a)
#define lson id<<1
#define rson id<<1|1
const int N = 5e5+7;
const int INF=0x3f3f3f3f;
struct node
{
int l,r,suf_L,pre_R;//要求的答案的左右端点,最大后缀和的左端点,最大前缀和的右端点
ll max_all,max_pre,max_suf,sum;//最大连续和,最大前缀和,最大后缀和,区间值;
}a[N<<1];
ll s[N<<1];
void pushup(int l,int r,int id){
a[id].max_pre=a[lson].max_pre;
a[id].pre_R=a[lson].pre_R;
a[id].max_suf=a[rson].max_suf;
a[id].suf_L=a[rson].suf_L;
a[id].sum=a[lson].sum+a[rson].sum;//sum用于求最大前缀和还有最大后缀和
//左区间sum+右区间 pre
a[id].max_all=a[lson].sum+a[rson].max_pre;
a[id].l=l;
a[id].r=a[rson].pre_R;
//左区间后缀suf+右区间 pre;
if(a[id].max_all<a[rson].max_pre+a[lson].max_suf||(a[id].max_all==a[rson].max_pre+a[lson].max_suf&&a[id].l>a[lson].suf_L)){
a[id].max_all=a[rson].max_pre+a[lson].max_suf;
a[id].l=a[lson].suf_L;
a[id].r=a[rson].pre_R;
}
//左区间的max_all
if(a[id].max_all<a[lson].max_all||(a[id].max_all==a[lson].max_all&&a[id].l==a[lson].l)){
a[id].max_all=a[lson].max_all;
a[id].r=a[lson].r;
a[id].l=a[lson].l;
}
//右区间的max_all;
if(a[id].max_all<a[rson].max_all){
a[id].max_all=a[rson].max_all;
a[id].l=a[rson].l;
a[id].r=a[rson].r;
}
//左区间的后缀suf+右区间的sum;
if(a[id].max_all<a[lson].max_suf+a[rson].sum||(a[id].max_all==a[lson].max_suf+a[rson].sum&&a[id].l>a[lson].suf_L)){
a[id].max_all=a[lson].max_suf+a[rson].sum;
a[id].l=a[lson].suf_L;
a[id].r=r;
}
//修改区间[l,r]的后缀和与前缀和的位置
if(a[id].max_suf<=a[lson].max_suf+a[rson].sum){//这里为什么时<=?因为题目要求要让x越小越好,如果有
a[id].max_suf = a[lson].max_suf+a[rson].sum;//多组解,那么y尽量越小越好;
a[id].suf_L = a[lson].suf_L;//在保证最大后缀的基础上,当然希望后缀的位置越小越好
}
if(a[id].max_pre<a[rson].max_pre+a[lson].sum){
a[id].max_pre = a[rson].max_pre+a[lson].sum;
a[id].pre_R=a[rson].pre_R;//这一行不能在取等号的前提下改变值了,因为前缀和一旦改变
} //就使其前缀的位置变大,也就是大于当前区间的左端点
}
void bulid(int l,int r,int id){
if(l==r){ll temp;
scanf("%lld",&temp);
a[id].max_all=temp;a[id].max_pre=temp;
a[id].max_suf=temp;a[id].sum=temp;
a[id].l=a[id].r=a[id].pre_R=a[id].suf_L=l;
return ;
}
int mid=(l+r)>>1;
bulid(l,mid,lson);
bulid(mid+1,r,rson);
pushup(l,r,id);
}
node qurry(int l,int r,int L,int R,int id){
if(l==L&&r==R) return a[id];//对于取值查询有可能是在线段树的一个区间,也有可能是在两个区间
//比如查询[1,4]就是一个线段树的区间,但是查询[1,5]的时候就需要到两个区间里面取寻找答案
int mid=(L+R)>>1;
if(r<=mid) return qurry(l,r,L,mid,lson);
else if(l>mid) return qurry(l,r,mid+1,R,rson);
else{//下面使求解当答案在两个区间的时候的求解情况
node t1=qurry(l,mid,L,mid,lson);
node t2=qurry(mid+1,r,mid+1,R,rson);
node t;
t.sum = t1.sum + t2.sum;
//这里的对比情况和上面的合并时一种思路;
t.max_pre=t1.max_pre;
t.pre_R=t1.pre_R;
t.max_suf=t2.max_suf;
t.suf_L=t2.suf_L;
//左区间总和+右区间最大前缀
t.max_all=t1.sum+t2.max_pre;
t.l=l;
t.r=t2.pre_R;
//左区间最大连续和
if(t.max_all<t1.max_all||(t.max_all==t1.max_all&&t.l==t1.l)){
t.max_all=t1.max_all;
t.r=t1.r;
t.l=t1.l;
}
//左区间最大后缀和+右区间最大前缀和
if(t.max_all<t1.max_suf+t2.max_pre||(t.max_all==t1.max_suf+t2.max_pre&&t.l>t1.suf_L)){
t.max_all=t1.max_suf+t2.max_pre;
t.l=t1.suf_L;
t.r=t2.pre_R;
}
//左区间最大后缀和+右区间的区间和
if(t.max_all<t1.max_suf+t2.sum||(t.max_all==t1.max_suf+t2.sum&&t.l>t1.suf_L)){
t.max_all=t1.max_suf+t2.sum;
t.l=t1.suf_L;
t.r=t2.r;
}
//右区间的最大连续和
if(t.max_all<t2.max_all ){//这个地方注意
t.max_all=t2.max_all;
t.l=t2.l;
t.r=t2.r;
}
//修改最大前缀与后缀的位置
if(t.max_pre<t1.sum+t2.max_pre){
t.max_pre=t1.sum+t2.max_pre;
t.pre_R=t2.pre_R;
}
if(t.max_suf<=t1.max_suf+t2.sum){
t.max_suf=t1.max_suf+t2.sum;
t.suf_L=t1.suf_L;
}
return t;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
int n,m;
int T=1;
while(scanf("%d%d",&n,&m)!=EOF){
bulid(1,n,1);
printf("Case %d:\n",T++);
while(m--){int l,r;
scanf("%d%d",&l,&r);
node t=qurry(l,r,1,n,1);
printf("%d %d\n",t.l,t.r);
}
}
return 0;
}
本文介绍了一种使用线段树解决区间最大子序列和问题的方法,通过构建线段树并进行区间查询,实现了对给定序列的高效求解。文章详细解释了线段树的构建和查询过程,包括如何维护区间的最大前缀和、最大后缀和及最大连续和,并通过代码示例展示了算法的具体实现。
1464

被折叠的 条评论
为什么被折叠?



