学过数据结构和会做题完全是两个概念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; }