zoukankan      html  css  js  c++  java
  • 【题解】SP2916 GSS5

    GSS5 = GSS1 + 前缀和 + 区间最值

    原题链接

    其实这道题感觉评分不能上紫

    建议先做一下GSS1再做这道题。

    好了,我们假设你已经搞懂了GSS1,现在开始讲GSS5(

    Case 1

    题目给了两个区间 ([x_1,y_1], [x_2,y_2]),先考虑一下两个区间不相交的情况。

    对原序列做一下前缀和,用线段树维护一下最大最小值,然后查询的时候就在 ([x_2,y_2]) 找最大,在 ([x_1-1,y_1-1]) 找最小,两者相减即可得到最大的子段和。

    Case 2

    如果两个区间会相交呢?显然这时候直接进行 ( ext{Case 1}) 操作是不正确的。我们对于左右端点所在的区间进行分类讨论。

    首先对 ([x_1,y_1])([y_1,y_2]) 进行 ( ext{Case 1}) 的操作,由于这里的区间只有一个元素相交,这么做不会出问题。

    然后同理地,对 ([x_1,x_2])([x_2,y_2]) 进行 ( ext{Case 1}) 的操作。

    现在只剩下了查询的两个区间的交集,即 ([x_2,y_1]) 尚未处理。我们发现,这是我们要找的就是区间最大子段和,于是就可以copy下来GSS1的代码(

    (small extbf{Case 1}+ extbf{Case 2} = color{green}{AC})

    交了两次才过qAq,看到SPOJ下的评论好多人都一次过了。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    int T,n,m,a[50005],sum[50005];
    typedef long long LL;
    const int INF=0x7fffffff;
    struct TreeNode
    {
        LL lmax,rmax,maxx;
        LL sum,mn,mx;
        TreeNode(): sum(0),mn(INF),mx(-INF)
            { lmax=rmax=maxx=-INF; }
        TreeNode(int)
            { lmax=rmax=maxx=sum=mn=mx=0; }
    }t[40005];
    inline void merge(TreeNode &dst,const TreeNode &a,const TreeNode &b)
    {
        dst.lmax=max(a.lmax,a.sum+b.lmax);
        dst.rmax=max(b.rmax,b.sum+a.rmax);
        dst.maxx=max({a.maxx,b.maxx,a.rmax+b.lmax});
        dst.sum=a.sum+b.sum;
        dst.mn=min(a.mn,b.mn),dst.mx=max(a.mx,b.mx);
    }
    // 用于合并两个区间
    #define LC (i<<1)
    #define RC (i<<1)|1
    void build(int l,int r,int i=1)
    {
        if(l==r)
        {
            t[i].sum=t[i].lmax=t[i].rmax=t[i].maxx=a[l];
            t[i].mn=t[i].mx=sum[l];
            // 赋初始值
        }
        else
        {
            int mid=(l+r)>>1;
            build(l,mid,LC);
            build(mid+1,r,RC);
            merge(t[i],t[LC],t[RC]);// 合并区间
        }
    }
    TreeNode query(int lq,int rq,int l=1,int r=n,int i=1)
    {
        if(lq==rq && lq==0) return TreeNode(0);
        // 由于线段树在0位置没有存值,需要特殊处理
        if(l>=lq && r<=rq)  return t[i];
        int mid=(l+r)>>1;
        TreeNode res,x,y;
        if(mid>=lq) x=query(lq,rq,l,mid,LC);
        if(mid<rq)  y=query(lq,rq,mid+1,r,RC);
        merge(res,x,y);
        if(lq<=0)   res.mx=max(res.mx,0ll),res.mn=min(res.mn,0ll);
        // 同理,对于0位置的处理
        return res;
    }
    main()
    {
        scanf("%d",&T);
        while(T--)
        {
            memset(a,0,sizeof(a));
            memset(sum,0,sizeof(sum));
            memset(t,0,sizeof(t));
            scanf("%d",&n);
            for(int i=1;i<=n;i++)   scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
            build(1,n); // 建树
            scanf("%d",&m);
            while(m--)
            {
                int l1,r1,l2,r2;
                scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
                LL ans;
                if(l2>r1)// Case 1,两区间不相交
                    ans=query(l2,r2).mx-query(l1-1,r1-1).mn;
                else// Case 2,两区间相交
                    ans=max({query(l2,r2).mx-query(l1-1,l2-1).mn,// 左右端点分别在[l1,l2],[l2,r2]
                             query(r1,r2).mx-query(l1-1,r1-1).mn,// 左右端点分别在[l1,r1],[r1,r2]
                             query(l2,r1).maxx});// 查询相交部分的最大子段和
                printf("%lld
    ",ans);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Java读书笔记
    b_aw_旅游计划(树的中心变形)
    b_lc_秋叶收集器(分类讨论dp+滚动数组优化)
    b_lg_涂色(从小区间做起,讨论s[l]和s[r]的关系)
    c_lc_早餐组合(排序+双指针)
    c_aw_鱼塘钓鱼(大根堆)
    b_pat_栈(2*multiset+stack)
    c_pat_推荐系统(set模拟)
    b_lg_时态同步(后序遍历统计每棵子树的最大高度)
    b_lc_统计不开心的朋友(预处理+模拟)
  • 原文地址:https://www.cnblogs.com/ExplodingKonjac/p/15066007.html
Copyright © 2011-2022 走看看