码迷,mamicode.com
首页 > 其他好文 > 详细

[hdu-6638]Snowy Smile 线段树维护 带修改的区间最大字段和 最大子矩阵 2019多校6

时间:2019-08-07 22:53:00      阅读:115      评论:0      收藏:0      [点我收藏+]

标签: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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!