【省选】算法总结——线段树2
来源:互联网 发布:mac上microsoft 编辑:程序博客网 时间:2024/06/11 21:08
130331总结——线段树2
前面总结了一次线段树,基本上讲完了所有的线段树操作
1. 区间修改(懒标记)
2. 插入/修改/删除 元素
3. 查询维护信息
4. 静态维护动态区间 (size)
总之,Operation和MinMax两道题基本上已经包括完了
上次总结基本是操作,这次主要是刚做了两道题,很有起始,所以总结一下线段树维护信息的方式和一些建树方法
1. 信息维护
顾名思义,如果题目很明了是线段树,那么他一定会有一些处理的技巧。
顺便把上次没说到的再说一次
1.1 最(大/小)值
这个很简单,直接用一个minx[]数组即可【注:还有一些人喜欢把线段树的所有信息定义为一个结构体,如 struct node{int l,r,info;}tree[N*4]; 其中info就是存我们想要的信息,这里是minx,这样也是可以的】,由于我们维护线段树是递归调用,所以在回溯的时候更新信息,比如修改操作可以向下面这样写
void change(int p,int l,int r,int a,int b,int c)
//将区间[a,b]修改为c ,维护区间最小值ֵ
{
if(a<=l&&b>=r){minx[p]=maxx[p]=c;return;}
int m=(l+r)>>1;
if(a<=m) change(p<<1,l,m,a,b,c);
if(b>m) change((p<<1)+1,m+1,r,a,b,c);
minx=min(minx[p<<1],minx[(p<<1)+1]);
}
其他查询操作同理
1.2 区间和
只需要把min[]数组改成sum[]即可,每次更新时sum[p]为左右儿子sum[]之和
1.3 区间中1的个数
这个时候就会用到懒标记,代码如下
void query(int p,int l,int r,int a,int b)
//找区间[a,b]有多少个1
//val[p]=1或0 表示区间[l,r]全部为1或0
//val[p]=-1 表示区间[l,r]中0和1均有
{
if(a<=l&&b>=r)
{
if(val[p]!=-1)return val[p]*(r-l+1);
}
int m=(l+r)>>1,x1=0,x2=0;
if(val[p]!=-1) //懒标记下传
{
val[p<<1]=val[(p<<1)+1]=val[p];
val[p]=-1;
}
if(a<=m) x1=query(p<<1,l,m,a,b);
if(b>m) x2=query((p<<1)+1,m+1,r,a,b);
if(val[p]==-1&&val[p<<1]==val[(p<<1)+1])
val[p]=val[p<<1]; //再把标记传上去
return x1+x2; //返回两个区间1的个数和
}
1.4 区间最大连续1的长度
这个问题就可以借鉴operation那一题了,由于连续的1在线段树中可能跨区间,所以我们可以用一个last记录上次找到的是1还是0,然后用best记录最优值,用ans记录当前找到的连续1的长度
void data_up(int p)
{
if(val[p<<1]==val[(p<<1)+1]&&val[p]==-1)
val[p]=val[p<<1];
}
void data_down(int p)//mark downloading
{
if(val[p]!=-1)
{
val[p<<1]=val[(p<<1)+1]=val[p];
val[p]=-1;
}
}
void find(int p,int l,int r,int a,int b)//find continuous 1
{
if(a<=l && b>=r)
{
if(val[p]!=-1)
{
if(last==1&& val[p]==1)
{
ans+=r-l+1;//We must add the last calculation answer
best=max(best,ans);
}
elseif(last==0&& val[p]==1)
{
ans=r-l+1;//We only need this calculation answer
best=max(best,ans);
}
elseif(val[p]==0) ans=0;//renew the answer
best=max(best,ans);
last=val[p];
return;
}
if(l==r)return;
}
data_down(p);
int m=(l+r)>>1;
if(a<=m) find(p<<1,l,m,a,b);
if(b>m) find((p<<1)+1,m+1,r,a,b);
data_up(p);
}
1.4 区间中最大连续子区间和
【小白逛公园】http://blog.csdn.net/jiangzh7/article/details/8745213
这一题看似dp,但是一看数据范围,很大,仔细看题,区间!所以,线段树!
但是怎么维护呢?建好树后每个区间来一个dp ?超时否?
所以另想办法。
1. 维护一个从最左开始的连续区间最大值L
2. 一个从最右开始向左的连续区间最大和R
3. 一个中间任意位置开始的连续区间最大值M
4. 最后再来一个区间和S
借鉴区间下dp的思路
如果我们把Q=[a,b]分成A=[a,m]和B=[m+1,b]两个区间,那么
1. Q.S = A.S + B.S
2. Q.L = max { A.L , A.S + B.L };
3. Q.R = max { B.R , B.S + A.R };
4. Q.M = max { Q.M , Q.M , A.R +B.L };
这样就可以维护线段树了,代码就不放了
还有一道应该比这个稍难吧,POJ上的,其实我NOIP集训的时候就做了,只不过是朴素过的50分
【POJ3667】http://blog.csdn.net/jiangzh7/article/details/8744258
2. 建树
暂时比较靠技巧的只见过两种,就把这两种说说吧
2.1 插入类
有一题是【[JSOI2008]最大数Maxnumber】
这一题本来很简单的,但是考试的时候不知道是哪根筋抽了,嗯是没看出来怎么做
虽然是说的插入元素,但是我们可以转换一下,先假设插入了MAXN个0,然后在进行修改,这样就ok了!
2.2 中国结类
这个名字是我自己取的。。。。。
主要是因为【RQ60美丽的中国结】一题,它的建树方法很是特别(至少我的第一次见),好在后来遇到了几次都很快的想到了这种方法
这一题只告诉你那些点是相连的,但是却不知道跟在哪里
dfs其实是把整棵树遍历一次,想想强连通那个时间戳dfstime ,我们可以类似的,把整棵树遍历一次,然后用L[x]和R[x]记录节点x进入dfs和退出dfs的时间,这样对于每个节点,我们都可以得到一个区间建立线段树了。
- 【省选】算法总结——线段树
- 【省选】算法总结——线段树2
- 线段树算法总结
- 算法模板——线段树
- C++ 线段树—模板&总结
- 数据结构与算法复习(3)—— 线段树
- 学习笔记——线段树算法学习
- 经典算法题——第十二题 线段树
- 算法模板——线段树之Lazy标记
- 周中训练笔记11——线段树总结
- 【省选】算法总结——划分树
- 【省选】算法总结——平衡树
- 算法导论学习-线段树(2)
- |算法讨论|线段树2 学习笔记
- 线段相交算法——平面扫描
- 算法--线段树
- 算法训练-线段树
- 算法摘记 线段树
- solaris xstartup using gnome
- 宏
- Maven下使用Jetty进行Debug
- 关于MFC 计算器响应键盘消息及虚拟键码
- The word is not correctly spelled问题解决(修改)
- 【省选】算法总结——线段树2
- 拍打
- 近三年,学习开发计划
- master_pos_wait函数与MySQL数据库主从复制切换
- 正则表达式
- windows与linux中文件路径分隔符问题解决--通过File.separator解决掉此问题
- IOS5 ARC unsafe_unretained等说明
- Eclipse CDT 调试C/C++程序设置
- Android--消息推送机制