学过数据结构和会做题完全是两个概念orz

各种各样的题目都应该见识一下

简单的目录:

维护多个值的线段树

吉司机线段树

线段树合并/分裂

 


 

维护多个值的线段树

 

最基本的应用是动态维护最大子段和

考虑对于一个$[L,R]$区间,被从正中分成两个子区间$[L,mid],[mid+1,R]$

那么$[L,R]$区间的最大子段区间只可能有三种来源:仅在$[L,mid]$中、仅在$[mid+1,R]$中、跨两个子区间

前两种来源很好处理,可以直接拿$[L,mid],[mid+1,R]$中的最大子段和来更新$[L,R]$中的

唯一比较麻烦的就是跨两个子区间的情况

此时,显然没有办法在只维护最大子段和的情况下解决这种情况,所以可以考虑多维护一些值

   1. $l[k]$,表示$k$所对应的区间中,从最左端开始的最大区间和

   2. $r[k]$,表示$k$所对应的区间中,从最右端开始的最大区间和

   3. $sum[k]$,表示$k$所对应的区间 的区间和

这样一来,跨区间的情况就可以通过$max(0,r[k<<1])+max(0,l[k<<1\text{|} 1])$表示,即从$[L,mid]$的右端、$[mid+1,R]$的左端各取最大的一段,并拼起来

而$l$的维护不是很复杂,$l[k]=max(l[k<<1],sum[k<<1]+l[k<<1\text{|} 1])$,即要不取$[L,mid]$的左端,要不全取$[L,mid]$、同时拼上$[mid+1,R]$的左端

$r$同理,$sum$比较简单就不赘述了

一道例题:HDU 6638 ($Snowy\ Smile$,$2019\ Multi-University\ Training\ Contest\ 6$)

将坐标离散化后,考虑先确定矩形在$x$轴上的范围

可以对于一个上界$i$,依次枚举下界$j$,这样就能不重不漏地枚举出$\frac{n(n+1)}{2}$种矩形的上下界

可以将每一列的元素求和后看成一个数组,于是问题就转化为了动态维护区间最大子段和

由于一共只有$n$个箱子,每个箱子只会在每个上界$i$中被压入线段树一次,所以总复杂度是$O(n^2\cdot logn)$

注意应该在压完一整行后再更新答案,因为同一行的箱子是会互相影响的

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N=2005;

int n;
int x[N],y[N],w[N];

void Change(int *a)
{
    vector<int> vec;
    for(int i=1;i<=n;i++)
        vec.push_back(a[i]);
    
    sort(vec.begin(),vec.end());
    vec.resize(unique(vec.begin(),vec.end())-vec.begin());
    
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(vec.begin(),vec.end(),a[i])-vec.begin()+1;
}

int sz;
ll t[N<<2],l[N<<2],r[N<<2],sum[N<<2];

inline void Add(int k,int x)
{
    k=k+sz-1;
    t[k]+=x;
    l[k]+=x;
    r[k]+=x;
    sum[k]+=x;
    k>>=1;
    
    while(k)
    {
        t[k]=max(t[k<<1],t[k<<1|1]);
        t[k]=max(t[k],max(r[k<<1],0LL)+max(l[k<<1|1],0LL));
        l[k]=max(l[k<<1],sum[k<<1]+l[k<<1|1]);
        r[k]=max(r[k<<1|1],sum[k<<1|1]+r[k<<1]);
        sum[k]=sum[k<<1]+sum[k<<1|1];
        
        k>>=1;
    }
}

vector<pii> v[N];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&x[i],&y[i],&w[i]);
        
        Change(x);
        Change(y);
        
        sz=1;
        while(sz<n)
            sz<<=1;
        
        for(int i=1;i<=n;i++)
            v[i].clear();
        for(int i=1;i<=n;i++)
            v[x[i]].push_back(pii(y[i],w[i]));
        
        
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            memset(t,0,sizeof(t));
            memset(l,0,sizeof(l));
            memset(r,0,sizeof(r));
            memset(sum,0,sizeof(sum));
            
            for(int j=i;j<=n;j++)
            {
                for(int k=0;k<v[j].size();k++)
                    Add(v[j][k].first,v[j][k].second);
                ans=max(ans,t[1]);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

相关文章: