Snowy Smile
题意简述:
给出平面上n(n <= 2000)个点,点的坐标在-1e9~1e9之间,试求出最大子矩阵的和是多少。
解题思路:
考虑数据范围,2e3个点离散化后可能会有2e3个,本题中各个点是离散的,所以用普通的动态规划比较麻烦,因此本题采用
O
(
N
2
l
o
g
2
N
)
O(N^2log_2N)
O(N2log2N)的线段树+枚举。
我们对x,枚举所有可能的x[i]和x[j]其中x[i]为上界,x[j]为下界,这个枚举花费时间为
O
(
N
2
)
O(N^2)
O(N2),而对于每一种可能的组合,再利用线段树
O
(
l
o
g
2
N
)
O(log_2N)
O(log2N)求出最大连续子段和,因此总时间
O
(
N
2
l
o
g
2
N
)
O(N^2log_2N)
O(N2log2N)。
代码示例:
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int N = 3010;
int t,n;
int tmp[N]; //离散化用
typedef long long ll;
struct Chest{
int x,y,val;
bool operator <(const Chest & B) const{
return x < B.x;
}
}cs[N];
struct SegmentTree{
/*分别为区间和,区间最大子段和,
从左侧开始的最大子段和,从右侧开始的最大子段和*/
ll sum,mx,lx,rx;
#define sum(x) tr[x].sum
#define mx(x) tr[x].mx
#define lx(x) tr[x].lx
#define rx(x) tr[x].rx
}tr[N*4];
void BuildTree(int rt,int l,int r){
/*在区间[l,r]上建立一棵线段树*/
sum(rt) = mx(rt) = lx(rt) = rx(rt) = 0;
if(l == r) return;
int mid = l+r>>1;
BuildTree(rt*2,l,mid);
BuildTree(rt*2+1,mid+1,r);
}
void spread(int rt){
/*用于根据rt的子节点更新rt的4个数据*/
sum(rt) = sum(rt*2)+sum(rt*2+1);
mx(rt) = max(rx(rt*2)+lx(rt*2+1),max(mx(rt*2),mx(rt*2+1)));
lx(rt) = max(lx(rt*2),sum(rt*2)+lx(rt*2+1));
rx(rt) = max(rx(rt*2+1),sum(rt*2+1)+rx(rt*2));
}
void Insert(int rt,int p,int val,int l,int r){
/*将线段树p位置的值设为val;l,r是辅助作用哦*/
if(l == r){
sum(rt) += val;
lx(rt) = rx(rt) = mx(rt) = sum(rt);
return;
}
int mid = l+r>>1;
if(p <= mid) Insert(rt*2,p,val,l,mid);
else Insert(rt*2+1,p,val,mid+1,r);
spread(rt); //在回溯时更新节点的值
}
ll ask(){
/*返回当前最大连续子段和*/
return tr[1].mx;
}
void solve(){
sort(tmp+1,tmp+1+n);
int cnt = unique(tmp+1,tmp+1+n)-tmp-1;
sort(cs+1,cs+1+n);
ll ans = 0;
/*我们对每一个x[i]上限,枚举所有可能x[j]的下限,
并利用线段树求出x[i]~x[j]最大字段和*/
for(int i = 1;i <= n;i++){
if(cs[i].x == cs[i-1].x && i != 1) continue;
BuildTree(1,1,cnt);
for(int j = i;j <= n;j++){
if(cs[j].x != cs[j-1].x && i != j) ans = max(ans,ask());
int y = lower_bound(tmp+1,tmp+1+cnt,cs[j].y)-tmp;
Insert(1,y,cs[j].val,1,cnt);
}
ans = max(ans,ask());//如果全部相等,在此处特判
}
printf("%lld\n",ans);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i = 1;i <= n;i++){
scanf("%d%d%d",&cs[i].x,&cs[i].y,&cs[i].val);
tmp[i] = cs[i].y;
}
solve();
}
}