This way
題意:
空間中有n個點,讓你選擇一個矩形覆蓋一些點或者不覆蓋任何點使得這個矩形中所有點的和最大。
題解:
雖然貌似它是個2000*2000的矩陣,但是它有的點只有2000個。所以我們先按照x軸排序,然后枚舉左端點的同時枚舉右端點,意思是固定左右端點,一定包括這兩個點的最大子矩陣是多少。用線段樹維護區間前綴和,然后查上面的最大前綴和-下面的最小前綴和,就像下圖:
但是有一個很重要的剪枝,也就是在每次查詢之前看看mx[1]-max(0ll,mi[1])是否小于等于ans,如果是的話就沒必要往下做了。雖然不嚴謹,但是對于答案是不會有影響的。
#include<bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std;
#define ll long long
const int N=2e3+5;
const ll inf=1e18;
int dis_x[N],dis_y[N];
ll mi[N*4],mx[N*4],flag[N*4],pre[N*4];
struct node
{int x,y;ll w;bool operator< (const node& a)const{return x<a.x;}
}p[N];
vector<node>have[N];
void push_down(int root)
{if(flag[root]==inf)return ;mi[root<<1]+=flag[root];mi[root<<1|1]+=flag[root];mx[root<<1]+=flag[root];mx[root<<1|1]+=flag[root];flag[root<<1]+=flag[root];flag[root<<1|1]+=flag[root];flag[root]=0;
}
void update(int l,int r,int root,int ql,int qr,ll v)
{if(l>=ql&&r<=qr){mi[root]+=v;mx[root]+=v;flag[root]+=v;return ;}push_down(root);int mid=l+r>>1;if(mid>=ql)update(l,mid,root<<1,ql,qr,v);if(mid<qr)update(mid+1,r,root<<1|1,ql,qr,v);mi[root]=min(mi[root<<1],mi[root<<1|1]);mx[root]=max(mx[root<<1],mx[root<<1|1]);
}
ll q_mi(int l,int r,int root,int ql,int qr)
{if(l>=ql&&r<=qr)return mi[root];push_down(root);int mid=l+r>>1;ll ans=inf;if(mid>=ql)ans=q_mi(l,mid,root<<1,ql,qr);if(mid<qr)ans=min(ans,q_mi(mid+1,r,root<<1|1,ql,qr));return ans;
}
ll q_mx(int l,int r,int root,int ql,int qr)
{if(l>=ql&&r<=qr)return mx[root];push_down(root);int mid=l+r>>1;ll ans=-inf;if(mid>=ql)ans=q_mx(l,mid,root<<1,ql,qr);if(mid<qr)ans=max(ans,q_mx(mid+1,r,root<<1|1,ql,qr));return ans;
}
int main()
{int t;scanf("%d",&t);while(t--){int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d%d%lld",&p[i].x,&p[i].y,&p[i].w),dis_x[i]=p[i].x,dis_y[i]=p[i].y,have[i].clear();sort(dis_x+1,dis_x+1+n);sort(dis_y+1,dis_y+1+n);int all_x=unique(dis_x+1,dis_x+1+n)-dis_x-1;int all_y=unique(dis_y+1,dis_y+1+n)-dis_y-1;for(int i=1;i<=n;i++){p[i].x=lower_bound(dis_x+1,dis_x+1+all_x,p[i].x)-dis_x;p[i].y=lower_bound(dis_y+1,dis_y+1+all_y,p[i].y)-dis_y;have[p[i].x].push_back(p[i]);}sort(p+1,p+1+n);ll ans=0;for(int i=1;i<=n;i++){for(int j=0;j<=4*all_y;j++)mi[j]=0,mx[j]=0,flag[j]=0;for(int j=i;j<=n;j++){if(p[j].x==p[j-1].x&&p[j].y==p[j-1].y)continue;if(p[j].x!=p[j-1].x||j==i)for(auto v:have[p[j].x])update(1,all_y,1,v.y,all_y,v.w);q_mi(1,all_y,1,1,all_y);if(mx[1]-min(0ll,mi[1])<=ans)continue;int l=min(p[i].y,p[j].y),r=max(p[i].y,p[j].y);ll maxn=q_mx(1,all_y,1,r,all_y);ll minn=l-1>0?q_mi(1,all_y,1,1,l-1):0ll;if(minn<=0)ans=max(ans,maxn-minn);elseans=max(ans,maxn);}}printf("%lld\n",ans);}return 0;
}
/*
10
7
1 1 -1000000000
1 2 2000000000
1 3 3000000000
2 2 -2000000000
2 3 -2000000000
3 2 4000000000
3 3 5000000000
10
8
1 3 20
1 5 -30
1 7 10
5 5 60
5 2 -140
5 1 120
4 1 -130
2 8 -1118
1 3 40
1 5 -30
1 7 10
5 5 60
5 2 -140
5 1 120
4 1 -130
2 8 -111*/
總結
以上是生活随笔為你收集整理的2019 Multi-University Training Contest 6 1005Snowy Smile —— 线段树的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。