标签:return lin 字段 最大子矩阵 oid 下标 mic 维护 相同
链接: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 }
[hdu-6638]Snowy Smile 线段树维护 带修改的区间最大字段和 最大子矩阵 2019多校6
标签:return lin 字段 最大子矩阵 oid 下标 mic 维护 相同
原文地址:https://www.cnblogs.com/conver/p/11318010.html