UVALive - 3938 分治,线段树,求动态最大连续和

UVALive - 3938

题意: 给出一个长度为n的整数序列D,你的任务是对m个询问作出回答。对于询问(a,b),需要找到两个下标x和y,使得a≤x≤y≤b,并且Dx+Dx+1+...+Dy尽量大。如果有多组满足条件的x和y,x应该尽量小。如果还有多解,y应该尽量小。

tags: 分治思想,线段树       大白书201

区别于静态最大连续和,只能用分治算法: 最优解要么完全在左半序列,要么完全在右半序列,要么跨越中点。

我们构造一棵线段树,维护 3 个值:最大连续和 max_sub ,最大前缀和 max_prefix , 最大后缀和 max_suffix 。按照上述分治算法维护即可。

因为这个题要求出标号,所以我们还要记录标号。 标号要求尽可能小,我们只要在维护的时候注意一下顺序就好了。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
#define  mid  (l+(r-l)/2)
typedef long long ll;
const int N = 500005;

struct Item {
    ll  sum, sub, prefix, suffix;
    int id1, id2, id3, id4;
} tr[N<<2];
ll  ai;
int n, m;
Item pushup(Item ro1, Item ro2)
{
    Item ret;
    ret.sum = ro1.sum+ro2.sum;  // sum
    ret.sub = ro1.sub;     // sub
    ret.id1=ro1.id1, ret.id2=ro1.id2;
    if(ret.sub < ro1.suffix+ro2.prefix) {
        ret.sub = ro1.suffix+ro2.prefix;
        ret.id1=ro1.id4, ret.id2=ro2.id3;
    }
    if(ret.sub < ro2.sub) {
        ret.sub = ro2.sub;
        ret.id1=ro2.id1, ret.id2=ro2.id2;
    }
    ret.prefix = ro1.prefix;   //prefix
    ret.id3 = ro1.id3;
    if(ret.prefix < ro1.sum+ro2.prefix) {
        ret.prefix = ro1.sum+ro2.prefix;
        ret.id3 = ro2.id3;
    }
    ret.suffix = ro1.suffix+ro2.sum;  //suffix
    ret.id4 = ro1.id4;
    if(ret.suffix < ro2.suffix) {
        ret.suffix = ro2.suffix;
        ret.id4 = ro2.id4;
    }
    return ret;
}
void build(int ro, int l, int r)
{
    if(l==r) {
        scanf("%lld", &ai);
        tr[ro].sum=tr[ro].sub=tr[ro].prefix=tr[ro].suffix=ai;
        tr[ro].id1=tr[ro].id2=tr[ro].id3=tr[ro].id4=l;
        return ;
    }
    build(ro<<1, l, mid);
    build(ro<<1|1, mid+1, r);
    tr[ro] = pushup(tr[ro<<1], tr[ro<<1|1]);
}
Item query(int ro, int l, int r, int ql, int qr)
{
    if(ql<=l && r<=qr) {
        return tr[ro];
    }
    if(qr<=mid) return query(ro<<1, l, mid, ql, qr);
    else if(mid<ql) return query(ro<<1|1, mid+1, r, ql, qr);
    else
        return pushup(query(ro<<1,l,mid,ql,qr), query(ro<<1|1,mid+1,r,ql,qr));
}
int main()
{
    int cas = 0;
    while(~scanf("%d%d", &n, &m))
    {
        printf("Case %d:\n", ++cas);
        build(1, 1, n);
        int a, b;  Item ans;
        while(m--)
        {
            scanf("%d %d", &a, &b);
            ans = query(1, 1, n, a, b);
            printf("%d %d\n", ans.id1, ans.id2);
        }
    }

    return 0;
}

一开始写的比较挫的代码

看了别人的代码修改了一下:

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
#define  mid  (l+(r-l)/2)
#define  PII  pair<int , int >
typedef long long ll;
const int N = 500005;

struct Item {
    PII sub, pre, suf;
    int l, r;
} tr[N<<2];
ll  ai, sum[N];
int n, m;
PII getMax(PII x, PII y) {
    ll  sumx=sum[x.se]-sum[x.fi-1], sumy=sum[y.se]-sum[y.fi-1];
    if(sumx!=sumy) return sumx>sumy ? x : y;
    return x<y ? x : y;
}
Item pushup(Item ro1, Item ro2)
{
    Item ret;
    ret.l=ro1.l, ret.r=ro2.r;
    ret.sub = getMax(ro1.sub, getMax(ro2.sub, MP(ro1.suf.fi, ro2.pre.se)));
    ret.pre = getMax(ro1.pre, MP(ro1.l, ro2.pre.se));
    ret.suf = getMax(ro2.suf, MP(ro1.suf.fi, ro2.r));
    return ret;
}
void build(int ro, int l, int r)
{
    if(l==r) {
        scanf("%lld", &ai);
        tr[ro].sub=tr[ro].pre=tr[ro].suf=MP(l,l);
        tr[ro].l=tr[ro].r=l;
        sum[l] = sum[l-1]+ai;
        return ;
    }
    build(ro<<1, l, mid);
    build(ro<<1|1, mid+1, r);
    tr[ro] = pushup(tr[ro<<1], tr[ro<<1|1]);
}
Item query(int ro, int l, int r, int ql, int qr)
{
    if(ql<=l && r<=qr)  return tr[ro];
    if(qr<=mid) return query(ro<<1, l, mid, ql, qr);
    else if(mid<ql) return query(ro<<1|1, mid+1, r, ql, qr);
    else
        return pushup(query(ro<<1,l,mid,ql,qr), query(ro<<1|1,mid+1,r,ql,qr));
}
void Init() {
    mes(sum, 0);
}
int main()
{
    int cas = 0;
    while(~scanf("%d%d", &n, &m))
    {
        Init();
        build(1, 1, n);
        printf("Case %d:\n", ++cas);
        int a, b;    Item ans;
        while(m--)
        {
            scanf("%d %d", &a, &b);
            ans = query(1, 1, n, a, b);
            printf("%d %d\n", ans.sub.fi, ans.sub.se);
        }
    }

    return 0;
}

原文地址:https://www.cnblogs.com/sbfhy/p/8453649.html

时间: 2024-10-08 18:37:41

UVALive - 3938 分治,线段树,求动态最大连续和的相关文章

【BZOJ4372】烁烁的游戏 动态树分治+线段树

[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠.皮皮鼠会被烁烁吸引,所以会一直待在节点上不动.烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠.大意:给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. In

UVALive 7148 LRIP【树分治+线段树】

题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值.如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询.同时每做完一颗子树的时候,用MN,MX对线段树进行更新.

线段树求逆序数方法 HDU1394&amp;&amp;POJ2299

为什么线段树可以求逆序数? 给一个简单的序列 9 5 3 他的逆序数是3 首先要求一个逆序数有两种方式:可以从头开始往后找比当前元素小的值,也可以从后往前找比当前元素大的值,有几个逆序数就是几. 线段树就是应用从后往前找较大值得个数.(一边更新一边查) 当前个数是 n = 10 元素   9  5   3 9先加入线段树,T[9]+=1:查从T[9]到T[10]比9大的值,没有sum = 0: 5 加入线段树,T[5] += 1,查从T[5]到T[10]比5大的值,有一个9,sum +=1: 3

杭电 1754 I Hate It(线段树求最值)

http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 38601    Accepted Submission(s): 15270 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某

2016年湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)

原题链接 http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1809 Description Bobo has a balanced parenthesis sequence P=p1 p2…pn of length n and q questions. The i-th question is whether P remains balanced after pai and pbi  swapped. Note that questions ar

UVALive 4730 Kingdom 线段树+并查集

题目链接:点击打开链接 题意见白书P248 思路: 先把读入的y值都扩大2倍变成整数 然后离散化一下 用线段树来维护y轴 区间上每个点的 城市数量和联通块数量, 然后用并查集维护每个联通块及联通块的最大最小y值,还要加并查集的秩来记录每个联通块的点数 然后就是模拟搞.. T^T绝杀失败题..似乎数组开小了一点就过了,== #include<stdio.h> #include<math.h> #include<vector> #include<string.h>

UVA - 11983 Weird Advertisement (线段树求并面积)

Description G Weird Advertisement Renat Mullakhanov (rem), one of the most talented programmers in the world, passed away on March 11, 2011. This is very sad news for all of us. His team went to ACM ICPC World Finals - 2004, placed 4th and won gold m

hdu1394--Minimum Inversion Number(线段树求逆序数,纯为练习)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 10326 Accepted Submission(s): 6359 Problem Description The inversion number of a given number sequence a1, a2, ..., an is t

HDU 1394 Minimum Inversion Number(线段树求逆序数)

题目地址:HDU 1394 这题可以用线段树来求逆序数. 这题的维护信息为每个数是否已经出现.每次输入后,都从该点的值到n-1进行查询,每次发现出现了一个数,由于是从该数的后面开始找的,这个数肯定是比该数大的.那就是一对逆序数,然后逆序数+1.最后求完所有的逆序数之后,剩下的就可以递推出来了.因为假如目前的第一个数是x,那当把他放到最后面的时候,少的逆序数是本来后面比他小的数的个数.多的逆序数就是放到后面后前面比他大的数的个数.因为所有数都是从0到n-1.所以比他小的数就是x,比他大的数就是n-

BNU 2418 Ultra-QuickSort (线段树求逆序对)

题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=2418 解题报告:就是给你n个数,然后让你求这个数列的逆序对是多少?题目中n的范围是n < 500000,所以,暴力是不行的.还是第一次学会用线段树求逆序数,这种方法的时间复杂度是n * log n,是不是很快呢,利用了线段树查询速度快的优势.具体的方法如下: 这里先说一下,如果输入的n个数不是连续的,也就是说把这n个数按从小到大的顺序排列起来不是连续的话,还要先离散化一下,其实也就是把