链接:acm.hdu.edu.cn/showproblem.php?pid=6638
题目大意:给n个点 的坐标和权值,画一个矩形(边平行于坐标轴),矩形的值为矩形内部和边界所有点的和,求最大的矩形的值。
题解:求最大的子矩阵和
因为点的坐标x,y范围太大,先离散,变为1~2000内的数
把x放到数组里排序去重,二分查找下标(y相同)
先说一开始想的几个TLE的方法
1.直接二维差分 维护二维前缀和 ,暴力枚举每一个矩形 O(N^4)
2.dp O(N^3)
求最大字段和的方法 dp[i]为以i为结尾的最大的字段和 dp[i]=max (dp[i-1]+a[i],a[i]);
二维的最大子矩阵的方法: 我们先维护每一列的前缀和
枚举矩形上下边界l,r,我可以O(1)求出l-r区间内每一列i的和y,直接差分 y=sum[r][i]-sum[l-1][i];
神奇的事发生了,每一列被压缩成了一个数,又变为了求最大字段和
3.正解:O(N^2 log(N))
我们在上一个方法枚举上下边界l,r 后,变为求这个上下边界的最大字段和;
在移动了一行下边界后,我们把新加入的一行的新点修改———带修改的最大字段和(我们可以用线段树来维护最大字段和)。
把n个点按横坐标排序,从前往后加入,就按行加入了。
线段树维护:
lm 左端点开始的最大字段和
rm 右端点开始的最大字段和
sm 这个区间的最大字段和
tm 这个区间的所有数字和

1 #include<bits/stdc++.h>
2 using namespace std;
3 #define lcnow now<<1
4 #define rcnow (now<<1)|1
5 int const maxn=2010;
6 long long n,sx[maxn],sy[maxn],mx,my,ans;
7 struct tree{
8 long long lm,rm,sm,tm;
9 }tr[maxn<<4];
10 struct node{
11 long long a, b,v;
12 }no[maxn];
13 inline long long get_num(){
14 char ch;
15 bool flag=false;
16 long long num=0;
17 ch=getchar();
18 while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
19 while(ch>='0'&&ch<='9'){num=(num<<3)+(num<<1)+ch-'0';ch=getchar();}
20 if(flag)return -1*num;
21 return num;
22 }
23 bool cmp2(node x,node y){
24 return x.a<y.a;
25 }
26 void gethash(){
27 sort(sx+1,sx+1+n);sort(sy+1,sy+1+n);
28 mx=unique(sx+1,sx+1+n)-sx-1;
29 my=unique(sy+1,sy+1+n)-sy-1;
30 long long ax,ay;
31 for(int i=1;i<=n;i++){
32 ax=lower_bound(sx+1,sx+1+mx,no[i].a)-sx;
33 ay=lower_bound(sy+1,sy+1+my,no[i].b)-sy;
34 no[i].a=ax,no[i].b=ay;
35 }
36 sort(no+1,no+1+n,cmp2);
37 }
38 void update(int now){//利用儿子更新父亲
39 tr[now].tm=tr[lcnow].tm+tr[rcnow].tm;
40 tr[now].lm=max(tr[lcnow].lm,tr[lcnow].tm+tr[rcnow].lm);
41 tr[now].rm=max(tr[rcnow].rm,tr[rcnow].tm+tr[lcnow].rm);
42 tr[now].sm=max(tr[lcnow].sm,tr[rcnow].sm);
43 tr[now].sm=max(tr[lcnow].rm+tr[rcnow].lm,tr[now].sm);
44 }
45 void build(int now,int l,int r){
46 if(l==r){
47 tr[now].lm=tr[now].rm=tr[now].sm=tr[now].tm=0;
48 return;
49 }
50 int mid=(l+r)>>1;
51 build(now<<1,l,mid);
52 build((now<<1)+1,mid+1,r);
53 update(now);
54 } //初始化
55
56 void change(int now,int l,int r,int x,int v){
57 //当前点编号now,当前点控制区间 [l,r],要修改的点编号x,加上v
58 if(l==r){
59 tr[now].tm=tr[now].lm=tr[now].rm=tr[now].sm=tr[now].sm+v;
60 return;
61 }
62 int mid=(l+r)>>1;
63 if(x<=mid)change(now<<1,l,mid,x,v);
64 else change((now<<1)+1,mid+1,r,x,v);
65 update(now);
66 }
67 void work(){
68 memset(tr,0,sizeof tr);
69 ans=0;
70 int k;
71 for(int i=1;i<=n;i++)if(i==1||no[i].a!=no[i-1].a){
72 build(1,1,my);
73 for(int j=i;j<=n;j=k){
74 for( k=j;k<=n&&no[k].a==no[j].a;k++){
75 change(1,1,my,no[k].b,no[k].v);
76 }
77 if(tr[1].sm>ans)ans=tr[1].sm;
78 }
79 }
80 }
81 int main(){
82 long long t;
83 t=get_num();
84 while(t--){
85 n=get_num();
86 for(int i=1;i<=n;i++){
87 no[i].a=get_num(),no[i].b=get_num();no[i].v=get_num();
88 sx[i]=no[i].a,sy[i]=no[i].b;
89 }
90 gethash();
91 work();
92
93 printf("%lld\n",ans);
94 }
95 return 0;
96 }
来源:https://www.cnblogs.com/conver/p/11318010.html