最大连续和(线段树+分治)

最大连续和
Time Limit: 1000 MS Memory Limit: 32768 K
Total Submit: 61(13 users) Total Accepted: 15(9 users) Rating: Special Judge: No
Description

给出一个长度为n的整数序列D,你的任务是对m个询问做出回答。对于询问(a,b),需要找到

两个下标x和y,使得a<=x<=y<=b,并且Dx+Dx+1+....+Dy尽量大。如果有多组满足条件的x和y,x

应尽量小。如果还有多解y也应尽量小。

Input

第一行是一个整数T,代表测试数据组数。

对于每组测试数据,第一行为一个整数(1<=n,m<=500000),即整数序列的查询的个数。

第二行包含n个整数,依次为D1,D2,...Dn的值。这些整数绝对值不超过10^9。以下m行

每行为一个查询,包含两个整数a和b。

Output

对于每组数据,对于每个查询输出一行包含两个整数x和y。

Sample Input
1 8 3 -2 3 4 -6 6 10 -7 100 1 8 3 5 6 8
Sample Output
2 8 5 5 6 8
Author
陈禹@HRBUST

一开始写出了这样的代码;

#include<iostream>
#include<cmath>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long  LONG;
LONG p[500000];
struct node
{
    LONG sum;
    int left,right;
};
node check(int i1,int i2)
{
    node no,max;
    no.sum=0;
    no.left=i1;
    no.right=i1-1;
    //max.sum=-9223372036854775808;
    max.sum=-5000000;
    max.left=i1;
    max.right=i1;
    int i;
    for(i=i1;i<=i2;i++)
    {
        no.sum+=p[i];
        no.right++;
    //    cout<<no.sum<<endl;
        if(no.sum>max.sum)
        {
            max=no;
        //    cout<<max.sum<<endl;
        }
        if(no.sum<0)
        {
            no.left=i+1;
            no.right=i;
            no.sum=0;
        }
    }
    return max;
}
int main()
{
    int T,m,n;
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            int i;
            scanf("%d%d",&n,&m);
            for(i=0;i<n;i++)
            {
                scanf("%ld",&p[i]);
            }
            int x,y;
            while(m--)
            {
                scanf("%d%d",&x,&y);
                node q=check(x-1,y-1);
                printf("%d %d\n",q.left+1,q.right+1);
            }
        }
    }
    return 0;
}

果断超时
后来想到线段树:

写出了代码,中间出了很多错误,基本都是脑残错误

原理:

每次需要比较左右子树

最大值的比较:

只需要比较三段,(1)max_left,(2)max_right(3)max_left_suffix+max_right_prefix

以最左边第一个元素为起点的最大值,为什么要更新他,为了使其父亲线段获得最大值

(1)比较 max_left_prefix与left_all+max_right_prefix

以最右边最后一个元素开始往左的最大值

(1)比较max_right_suffix与right_all+max_left_suffix

#include<cstdio>
#include<cmath>
#include<queue>
#include<iostream>
using namespace std;
const int SIZE=500005;
typedef long  LL;
struct node
{
    LL sum,all;
    int pre_r,suf_l;
    int max_l,max_r;
    LL prefixSum,suffixSum;
} p[SIZE<<2];
LL a[SIZE];

void pushup(int L,int R,int rt)
{
    int ll=rt<<1;
    int rr=rt<<1|1;
    p[rt].all=p[ll].all+p[rr].all;

    p[rt].prefixSum=p[ll].prefixSum;
    p[rt].pre_r=p[ll].pre_r;

    p[rt].suffixSum=p[rr].all+p[ll].suffixSum;
    p[rt].suf_l=p[ll].suf_l;

    p[rt].sum=p[ll].sum;
    p[rt].max_l=p[ll].max_l;
    p[rt].max_r=p[ll].max_r;
    if(p[rt].sum<p[rr].sum)
    {
        p[rt].sum=p[rr].sum;
        p[rt].max_l=p[rr].max_l;
        p[rt].max_r=p[rr].max_r;
    }
    if((p[rt].sum<p[ll].suffixSum+p[rr].prefixSum)||
        (p[rt].sum==p[ll].suffixSum+p[rr].prefixSum&&p[rt].max_l>p[ll].suf_l))
    {
        p[rt].sum=p[ll].suffixSum+p[rr].prefixSum;
        p[rt].max_l=p[ll].suf_l;
        p[rt].max_r=p[rr].pre_r;
    }
    if(p[rt].prefixSum<p[ll].all+p[rr].prefixSum)
    {
        p[rt].prefixSum=p[ll].all+p[rr].prefixSum;
        p[rt].pre_r=p[rr].pre_r;
    }
    if(p[rt].suffixSum<p[rr].suffixSum)
    {
        p[rt].suffixSum=p[rr].suffixSum;
        p[rt].suf_l=p[rr].suf_l;
    }
}
void build(int L,int R,int rt)
{
    if(L==R)
    {
        p[rt].all=p[rt].sum=a[L];
        p[rt].prefixSum=a[L];
        p[rt].suffixSum=a[L];
        p[rt].max_l=L;
        p[rt].max_r=L;
        p[rt].pre_r=L;
        p[rt].suf_l=L;
        return ;
    }
    int m=(L+R)>>1;
    int ll=rt<<1;
    int rr=rt<<1|1;
    build(L,m,ll);
    build(m+1,R,rr);
    pushup(L,R,rt);
}
node query(int L,int R,int l,int r,int rt)
{
    if(L==l&&R==r)
    {
        return p[rt];
    }
      /*  if(L==R)   这一段时不可取的,这回大大投稿时间复杂度
       {
              return p[rt];
        }*/
    int m=(L+R)>>1;
    int ll=rt<<1;
    int rr=rt<<1|1;
    if(m<l)
    {
        return query(m+1,R,l,r,rr);
    }
    if(m>=r)
    {
        return query(L,m,l,r,ll);
    }
    node x=query(L,m,l,m,ll);
    node y=query(m+1,R,m+1,r,rr);
    node z;
    z.all=x.all+y.all;

    z.sum=x.sum;
    z.max_l=x.max_l;
    z.max_r=x.max_r;

    z.prefixSum=x.prefixSum;
    z.pre_r=x.pre_r;

    z.suffixSum=x.suffixSum+y.all;
    z.suf_l=x.suf_l;
    if(z.sum<y.sum)
    {
        z.sum=y.sum;
        z.max_l=y.max_l;
        z.max_r=y.max_r;
    }
    if((z.sum<x.suffixSum+y.prefixSum)||
    (z.sum<x.suffixSum+y.prefixSum&&z.max_l>x.suf_l))
    {
        z.sum=x.suffixSum+y.prefixSum;
        z.max_l=x.suf_l;
        z.max_r=y.pre_r;
    }
    if(z.prefixSum<x.all+y.prefixSum)
    {
        z.prefixSum=x.all+y.prefixSum;
        z.pre_r=y.pre_r;
    }
    if(z.suffixSum<y.suffixSum)
    {
        z.suffixSum=y.suffixSum;
        z.suf_l=y.suf_l;
    }
    return z;
}
int main()
{
    int T;
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            int n,m;
            scanf("%d%d",&n,&m);
            int i;
            for(i=1;i<=n;i++)
            {
                scanf("%ld",&a[i]);
            }
            build(1,n,1);
            while(m--)
            {
                int L,R;
                scanf("%d%d",&L,&R);
                node q=query(1,n,L,R,1);
                printf("%d %d\n",q.max_l,q.max_r);
            }
        }
    }
    return 0;
}        
时间: 2024-08-04 09:52:34

最大连续和(线段树+分治)的相关文章

3237: [Ahoi2013]连通图 线段树分治

题解: 线段树分治裸题 apio t1是这个所以就学习了一下 #include <bits/stdc++.h> using namespace std; const int N=2e5+10; struct re{ int x,y; }a[N*2]; int b[N+20][5],cnt,now,n,m,k; int ls[N*15],rs[N*15],data[N*15],last[N+20]; int ph[N*4],pt[N*4],count2[N+20],f[N]; bool ft[N

线段树分治总结(线段树分治,线段树,并查集,树的dfn序,二分图染色)

闲话 stO猫锟学长,满脑子神仙DS 线段树分治思想 我们在做CDQ的时候,将询问和操作通通视为元素,在归并过程中统计左边的操作对右边的询问的贡献. 而在线段树分治中,询问被固定了.按时间轴确定好询问的序列以后,我们还需要所有的操作都会影响一个时间区间.而这个区间,毫无疑问正好对应着询问的一段区间. 于是,我们可以将每一个操作丢到若干询问里做区间修改了,而线段树可以高效地维护.我们开一个叶子节点下标为询问排列的线段树,作为分治过程的底层结构. 具体的实现,仍然要看题目. 例题1 BZOJ4025

算法学习——动态图连通性(线段树分治+按秩合并并查集)

在考场上遇到了这个的板子题,,,所以来学习了一下线段树分治 + 带撤销的并查集. 题目大意是这样的:有m个时刻,每个时刻有一个加边or撤销一条边的操作,保证操作合法,没有重边自环,每次操作后输出当前图下所有联通块大小的乘积. 首先观察到如果没有撤销操作,那么直接用并查集就可以维护,每次合并的时候乘上要合并的两个并查集大小的逆元,然后乘上合并之后的大小即可. 那么来考虑撤销,观察到如果并查集不带路径压缩,应该是可以处理撤销操作的. 但我们并不能直接做,因为并查集的撤销必须按顺序来,就相当于每次合并

【线段树分治 线性基】luoguP3733 [HAOI2017]八纵八横

不知道为什么bzoj没有HAOI2017 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路的两端都是城市(可能两端是同一个城市),保证任意两个城市都可以通过高速公路互达. 国正在筹划“八纵八横”的高铁建设计划,计划要修建一些高速铁路,每条高速铁路两端也都是城市(可能两端是同一个城市),也都有一个非负整数的经济影响因子.国家还计划在“八纵八横”计划建成之后,将“一带一路”扩展为“一带_路一

bzoj 4137 [FJOI2015]火星商店问题——线段树分治+可持久化01trie树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4137 关于可持久化01trie树:https://www.cnblogs.com/LadyLex/p/7281110.html 看了看它的两道例题,就没写. 特殊商品可以直接用可持久化trie做. 其他部分用线段树分治.修改是单点的,询问是区间,原来想的是把询问区间定位后有 mlogn 个,在线段树的每个叶子上贡献一番:结果TLE了,因为若是在叶子处贡献,一个询问就要做 r-l+1 次.

loj#2312. 「HAOI2017」八纵八横(线性基 线段树分治)

题意 题目链接 Sol 线性基+线段树分治板子题.. 调起来有点自闭.. #include<bits/stdc++.h> #define fi first #define se second #define pb push_back #define bit bitset<B + 1> using namespace std; const int MAXN = 501, B = 1001, SS = 4001; inline int read() { char c = getchar

HAOI2017 八纵八横——线段树分治+线性基

题目大意 给定一个图,每次加一些边,或者删掉一些后来加上去的边,定义一个环的价值为环上所有的边的异或和,重复走的边重复算.每次询问这个时刻图中的所有经过1号点的环的最大价值. 思路 首先考虑对于一个静态的图如何求解图中所有经过1号点的环的最大价值,发现这个经过1号点就是唬人的,图中任意一个环都可以经过1号点再走回来. 于是题目变成了求解图中环的最大价值,可以将图中所有的简单环给拎出来放到线性基里面求最大价值,不难发现这是对的. 然后题目转化为了如何求图中所有的简单环,一般我们可以直接对图dfs找

bzoj4311向量(线段树分治+斜率优化)

第二道线段树分治. 首先设当前向量是(x,y),剩余有两个不同的向量(u1,v1)(u2,v2),假设u1>u2,则移项可得,若(u1,v1)优于(u2,v2),则-x/y>(v1-v2)/(u1-u2),然后维护上凸壳后进行三分即可,复杂度O(nlog2n),如果将询问排序扫一遍,可以优化到O(nlogn),当然我没写. #include<bits/stdc++.h> #define lson l,mid,rt<<1 #define rson mid+1,r,rt&l

线段树分治

2014徐寅展论文<线段树在一类分治问题上的应用>读后感. 线段树分治 线段树分治其实就是有撤销操作的时间分治. 题目让你维护一些信息,每次可以询问,可以执行一种操作,也可以将之前的某个这种操作撤回. 操作容易维护,但撤回操作不容易维护. 需要将操作,询问都离线下来.将时间轴画出来,那么每个操作只在时间轴上的一个区间内生效. 用线段树给这个区间打上这个操作的标记,维护信息. TJOI2018 数学计算 小豆现在有一个数x,初始值为1. 小豆有Q次操作,操作有两种类型: m: x = x * m

线段树分治总结

目录 类型一 例题1:八纵八横 代码: 例题2:时空旅行 首先,要求可以离线. 线段树分治有两种. 类型一 操作基于区间,单点询问. 有时,进行的一种操作可以快速完成,但是,要实现这种操作的逆操作较难. 因为,通常情况下,需要实现的逆操作都是很久以前执行的. 但是,如果只撤销上次操作,就会简单得多. 比如,维护一些连通性,或直径,线性基等问题. 这类问题加边很好做,但删边很难实现. 我们可以扫一遍操作,得到每个操作的有效区间. 然后,将每个添加操作的有效区间按在线段树上,然后遍历这颗线段树同时处