博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线段树复习
阅读量:6173 次
发布时间:2019-06-21

本文共 5191 字,大约阅读时间需要 17 分钟。

2017.3.24

T1   最大子段和 

初做:2017.2.1  time:2576ms   memory:22MB

现在:2017.3.24  time:2991ms  memory:29MB

#include
#include
#include
#define N 200001using namespace std;int n,m,tot;struct node{ int l,r,siz; long long lmax,rmax,maxx,sum;}tr[N*2];void up(int k){ int l=k+1,r=k+tr[k+1].siz*2; tr[k].lmax=max(tr[l].lmax,tr[l].sum+tr[r].lmax); tr[k].rmax=max(tr[r].rmax,tr[l].rmax+tr[r].sum); tr[k].sum=tr[l].sum+tr[r].sum; long long tmp1=max(tr[l].maxx,tr[r].maxx); long long tmp2=max(tr[k].lmax,tr[k].rmax); long long tmp3=max(tmp1,tmp2); tr[k].maxx=max(tmp3,tr[l].rmax+tr[r].lmax);}void build(int l,int r){ tr[++tot].l=l;tr[tot].r=r; tr[tot].siz=r-l+1; int k=tot; if(l==r) { cin>>tr[tot].maxx; tr[tot].lmax=tr[tot].rmax=tr[tot].sum=tr[tot].maxx; return; } int mid=l+r>>1; build(l,mid);build(mid+1,r); up(k);}void query(int k,int opl,int opr,long long & ans_l,long long &ans_r,long long &ans_sum,long long &ans){ if(tr[k].l==opl&&tr[k].r==opr) { ans_l=tr[k].lmax; ans_r=tr[k].rmax; ans_sum=tr[k].sum; ans=tr[k].maxx; return; } int mid=tr[k].l+tr[k].r>>1,l=k+1,r=k+tr[k+1].siz*2; if(opr<=mid) query(l,opl,opr,ans_l,ans_r,ans_sum,ans); else if(opl>mid) query(r,opl,opr,ans_l,ans_r,ans_sum,ans); else { long long lch_lmax,lch_rmax,lch_sum,lch_maxx; long long rch_lmax,rch_rmax,rch_sum,rch_maxx; query(l,opl,mid,lch_lmax,lch_rmax,lch_sum,lch_maxx); query(r,mid+1,opr,rch_lmax,rch_rmax,rch_sum,rch_maxx); ans_l=max(lch_lmax,lch_sum+rch_lmax); ans_r=max(rch_rmax,rch_sum+lch_rmax); ans_sum=lch_sum+rch_sum; long long tmp1=max(lch_maxx,rch_maxx); long long tmp2=max(ans_l,ans_r); long long tmp3=max(tmp1,tmp2); ans=max(tmp3,lch_rmax+rch_lmax); }}int main(){ freopen("data","r",stdin); freopen("2.out","w",stdout); scanf("%d",&n); build(1,n); scanf("%d",&m); int x,y; long long ans_l,ans_r,ans_sum,ans; while(m--) { scanf("%d%d",&x,&y); query(1,x,y,ans_l,ans_r,ans_sum,ans); printf("%lld\n",ans); }}
View Code

画蛇添足:

tmp1=max(lch_max,rch_max)

tmp2=max(l_max,r_max)

tmp3=max(tmp1,tmp2)

ans=max(tmp3,l_rmax+r_lmax)

优化:ans=max(tmp1,l_rmax+r_lmax)

原因:如果全部的左子区间+右子区间左半部分最优,那么左子区间的右半部分=全部的左子区间

加深理解:

ans_l=max(lch_lmax,lch_sum+rch_lmax);

ans_r=max(rch_rmax,rch_sum+lch_rmax);

ans_l != tr[l].lmax

ans_r != tr[r].rmax

ans_l是自下一层开始递归,直至找到符合要求的tr[].lmax

这个符合要求的tr[].lmax不一定就是当前左子区间的lmax

疑问:

既然如此,那lch_sum也应该是符合要求的tr[].sum,而不一定当前左子区间的sum

但如若直接用tr[l].sum代替lch_sum也能AC,且对拍无误

why?

 

T2 

只有0和1,找到连续0的个数超过x的位置,输出最左端,支持区间修改操作

初做:2017.2.1  time:354ms   memory:68.18MB

多开了10倍的结构体,重测:21.24MB

现在:2017.3.24  time:1030ms  memory:18.63MB

#include
#include
#define N 50001using namespace std;int n,m,tot,ans;struct node{ int l,r,lmax,rmax,maxx,siz; int f;}tr[N*2];void build(int l,int r){ int k=++tot; tr[k].l=l;tr[k].r=r; tr[k].lmax=tr[k].rmax=tr[k].maxx=tr[k].siz=r-l+1; if(l==r) return; int mid=l+r>>1; build(l,mid);build(mid+1,r);}void down(int k){ int l=k+1,r=k+tr[k+1].siz*2; if(tr[k].f==1) { tr[l].lmax=tr[l].rmax=tr[l].maxx=0; tr[r].lmax=tr[r].rmax=tr[r].maxx=0; } else { tr[l].lmax=tr[l].rmax=tr[l].maxx=tr[l].siz; tr[r].lmax=tr[r].rmax=tr[r].maxx=tr[r].siz; } tr[l].f=tr[r].f=tr[k].f; tr[k].f=0;}int query(int k,int l,int x){ if(tr[k].lmax>=x) return l; if(tr[k].f) down(k); int mid=tr[k].l+tr[k].r>>1,lc=k+1,rc=k+tr[k+1].siz*2; if(tr[lc].maxx>=x) return query(lc,l,x); if(tr[lc].rmax+tr[rc].lmax>=x) return mid-tr[lc].rmax+1; return query(rc,mid+1,x);}void up(int k){ int l=k+1,r=k+tr[k+1].siz*2; if(tr[l].lmax==tr[l].siz) tr[k].lmax=tr[l].siz+tr[r].lmax; else tr[k].lmax=tr[l].lmax; if(tr[r].rmax==tr[r].siz) tr[k].rmax=tr[l].rmax+tr[r].siz; else tr[k].rmax=tr[r].rmax; tr[k].maxx=max(max(tr[l].maxx,tr[r].maxx),tr[l].rmax+tr[r].lmax);}void change(int k,int opl,int opr,int w){ if(tr[k].l>=opl&&tr[k].r<=opr) { if(w==1) tr[k].lmax=tr[k].rmax=tr[k].maxx=0; else tr[k].lmax=tr[k].rmax=tr[k].maxx=tr[k].siz; tr[k].f=w; return; } if(tr[k].f) down(k); int mid=tr[k].l+tr[k].r>>1,l=k+1,r=k+tr[k+1].siz*2; if(opl<=mid) change(l,opl,opr,w); if(opr>mid) change(r,opl,opr,w); up(k);}int main(){ scanf("%d%d",&n,&m); build(1,n); int x,y,z; while(m--) { scanf("%d",&z); if(z==1) { scanf("%d",&x); if(tr[1].maxx
View Code

3个错误:

① 父节点编号k,左子节点为k+1,右子节点为k+tr[k+1].siz*2

没有+1,因为左子树节点数为2*tr[k+1].siz-1

② 区间修改为1时,实际操作区间opl,opr与当前递归区间l,r混淆

既然记录了siz信息,为啥不直接用呢

③ query时忘了下传标记

思路卡壳处:

 要求输出最左端,而线段树里没有记录这一信息。

可以在调用函数时设一实参表示,lmax满足要求时直接返回这一参数,这一点后来想到了

因为输出最左端,所以跨左右区间的优于在右子区间的

这一点在没过样例之后想到了,但如何处理

一开始的想法是递归左子区间,找>=x-tr[r].lmax的位置

真是脑抽了,明显不对

没忍住看了以前做的,是直接返回mid-tr[l].rmax+1

因为如果跨区间,那么最左端位置就固定了

以前写的跟这次的不大一样,仔细想想,他主要集中在了这一句

if(e[(k<<1)+1].max_l+e[k<<1].max_r>=x) return mid-e[k<<1].max_r+1;

(以前的代码)

 

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/6612836.html

你可能感兴趣的文章
开源力量公开课第二十三期-从SVN到Git,次时代代码管理
查看>>
输入挂
查看>>
升级迁移前,存储过程统计各个用户下表的数据量,和迁移后的比对
查看>>
sql注入分类
查看>>
初识CSS选择器版本4
查看>>
[Hadoop in China 2011] 朱会灿:探析腾讯Typhoon云计算平台
查看>>
JavaScript之数组学习
查看>>
PHP 设置响应头来解决跨域问题
查看>>
CAS实现SSO单点登录原理
查看>>
博客园美化专用图片链接
查看>>
HDU_1969_二分
查看>>
高等代数葵花宝典—白皮书
查看>>
一种简单的图像修复方法
查看>>
基于DobboX的SOA服务集群搭建
查看>>
C#设计模式之装饰者
查看>>
[noip模拟20170921]模版题
查看>>
获取ip
查看>>
Spring Shell简单应用
查看>>
移动app可开发的意见于分析
查看>>
周总结7
查看>>