磨了一天的线段树,不能说完全搞清楚,只能说有一个大概的了解,靠着模板才把这道题A了,只能说太弱~~!

题意:

初始时有一字符串,全为0.

三种操作:

1 k d - add  把d加到第k个数上去
2 l r - query sum 计算l到r所有数的和
3 l r - change to nearest Fibonacci 把l到r的数修改为距离它最近的斐波那契数

节点附件三个值:

s1:由lazy控制的区间的正确的和。

s2:区间内与所有数相近的fib数之和,随着单点更新而更新。

col:lazy,标记区间是否全部取fib数,是取1,否则取0。

询问区间的和时,找到相应区间直接返回s1,若有col为1的区间要先向下推送,表示要取该区间的fib数的和。

代码以及详解:

 #include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxm=;
long long f[]= {,};
struct node
{
long long e,f;
int flag;
}a[maxm<<];
void pushup(int rt)
{
a[rt].e=a[*rt].e+a[*rt+].e;
a[rt].f=a[*rt].f+a[*rt+].f;
}
void pushdown(int rt)
{
if(a[rt].flag)
{
a[*rt].flag=;
a[*rt+].flag=;
a[*rt].e=a[*rt].f;
a[*rt+].e=a[*rt+].f;
a[rt].flag=;
}
} //建树
void build(int l,int r,int rt)
{
a[rt].flag=;//先都初始化为0
a[rt].e=;//区间和初始化为0
if(l==r)//如果只有一个元素
{
a[rt].f=;//该节点的斐波那契数记为1,因为此时距离a[rt].e最近的斐波那契数为1
return;
}
int m=(l+r)/;//递归构造左右子树
build(l,m,rt<<);
build(m+,r,rt<<|);
pushup(rt);//回溯更新父节点
} void add(int pos,int m,int l,int r,int rt)
{//要修改的元素的位置是pos,加数为m
if(pos<l||pos>r)//如果pos不在区间范围内,则返回
return ;
if(l==r)//找到那个节点了
{
if(a[rt].flag)//如果这个节点是被处理过的,就是说它以及它的子孩子都已经是离它最近的斐波那契数了
{
a[rt].e=m+a[rt].f;//该节点的区间和为该节点的斐波那契数+m
a[rt].flag=;//由于节点的值已经修改过了,所以当前的a[rt].f不一定是正确的值了
}
else
a[rt].e+=m;//没有被处理过,直接相加就好了
int p=lower_bound(f,f+,a[rt].e)-f;//二分查找:p是小于a[rt].e的第一个数
if(!p)//如果不存在比a[rt].f大的数,那么离它最近的斐波那契数就是1了
a[rt].f=;
else if(abs(a[rt].e-f[p])<abs(a[rt].e-f[p-]))//比较第p个数和p-1个数哪个离a[rt].e近,把a[rt].f赋为较近的那个
a[rt].f=f[p];
else
a[rt].f=f[p-];
return;
}
pushdown(rt);//延迟标记法,判断孩子节点是否需要更新,需要更新就更新
int mid=(l+r)/;
if(pos<=mid)
add(pos,m,l,mid,*rt);
else
add(pos,m,mid+,r,*rt+);
pushup(rt);//回溯更新根节点
} //将l到r的数赋为离他最近的斐波那契数
void change(int L,int R,int l,int r,int rt)
{
if(R<l||L>r)
return;
if (L <= l && r <= R)
{
a[rt].e=a[rt].f;//将e的值修改为f的值
a[rt].flag=;//标记修改过了
return ;
}
pushdown(rt);//向下更新子节点
int m = (l + r) >> ;
if (L <= m) change(L , R, l,m,rt<<);
if (m < R) change(L , R ,m+,r,rt<<|);
pushup(rt);//回溯更新父节点
} //计算l到r个数的和
long long query(int L,int R,int l,int r,int rt)
{
if(R<l||L>r)
return ;
else if (L <= l && r <= R)
{
return a[rt].e;
}
pushdown(rt);//向下更新子节点
int m = (l + r) >> ;
long long ret = ;
if (L <= m) ret += query(L , R , l,m,rt*);
if (m < R) ret += query(L , R , m+,r,rt*+);
return ret;
}
int main()
{
int n,m,i,op;
int l,r;
int k,d;
for(i=; i<=; i++)//打出斐波那契表
f[i]=f[i-]+f[i-];
while(scanf("%d%d",&n,&m)!=EOF)
{
build(,n,);//建树
while(m--)//m种操作
{
scanf("%d",&op);
if(op==)
{
scanf("%d%d",&k,&d);
add(k,d,,n,);//给第k个数加上d;
}
else
{
scanf("%d%d",&l,&r);
if(op==)
printf("%I64d\n",query(l,r,,n,));//计算l到r个数的和
else
change(l,r,,n,);//将l到r的数赋为离他最近的斐波那契数
}
}
}
return ;
}

现附上线段树的一些基本概念以及两个常用模板:

  

线段树详解

1.概述

线段树类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,每个操作的复杂度为O(lgN)。

线段树的性质:若父区间是[a,b].(c=(a+b)/2),左儿子区间为[a,c],右儿子区间为[c+1,b],线段树需要的空间是数组大小的四倍。

2.基本操作

(1)线段树的构造(void build(int node,int begin,int end);)

运用递归的思想,如果当前结点记录的区间只有一个值,则直接赋值,                      否则递归左右子树。

代码如下:

#include <iostream>

using namespace std;

const int maxind = 256;

int segTree[maxind * 4 + 10];  //用来存储线段树,大小要大于原数组的四倍

int array[maxind];

/* 构造函数,得到线段树 */

void build(int node, int begin, int end)//node记录的是线段树的节点号

{

if (begin == end)

segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */

else

{

/*递归构造左右子树 */

build(2*node, begin, (begin+end)/2);//构造左孩子

build(2*node+1, (begin+end)/2+1, end);//构造右孩子

/*回溯时得到当前node节点的线段信息 */

if(segTree[2 * node] <= segTree[2 * node + 1])//把较小的数赋给左孩子

segTree[node] = segTree[2 * node];

else

segTree[node] = segTree[2 * node + 1];

}

}

int main()

{

array[0] = 1, array[1] = 2,array[2] = 2, array[3] = 4, array[4] = 1, array[5] = 3;

build(1, 0, 5);

for(int i = 1; i<=20; ++i)

cout<< "seg"<< i << "=" <<segTree[i] <<endl;

return 0;

}

(2)区间查询(int query(int node,int begin,int end,int left,int right);)

主要思想是把所要查询的区间[a,b],划分为线段树上的结点,然后将这些结点代表的区间合并起来得到所需要的信息。

代码如下:

//其中node为当前查询结点,begin,end为当前存储的区间,left,right为此次query所要查询的区间。

int query(int node, int begin, int end, int left, int right)

{

int p1, p2;

/*  查询区间和要求的区间没有交集  */

if (left > end || right < begin)

return -1;

/* 如果当前区间在查询区间内 ,返回该节点的值*/

if (begin >= left && end <= right)

return segTree[node];

/*  比较左右子树两个节点值的最小值  */

p1 = query(2 * node, begin, (begin + end) / 2, left, right);

p2 = query(2 * node + 1, (begin + end) / 2 + 1, end, left, right);

if (p1 == -1)

return p2;

if (p2 == -1)

return p1;

if (p1 <= p2)

return  p1;

return  p2;

}

这样的过程一定选出了尽量少的区间,他们连后一定涵盖整个[left,right],没有重复也没有遗漏,线段树并不适合所有区间查询情况,它使用的条件是“相邻区间的信息可以被合并成两个区间合并的信息”,即问题是可以被分解解决的。

(3)区间或结点的更新 及线段树的动态维护(线段树的核心价值所在)

A.单节点更新

void Updata(int node, int begin, int end, int ind, int add)/*单节点更新*/

{

if( begin == end )

{

segTree[node] += add;

return ;

}

int m = ( left + right ) >> 1;

if(ind <= m)

Updata(node * 2,left, m, ind, add);

else

Updata(node * 2 + 1, m + 1, right, ind, add);

/*回溯更新父节点*/

segTree[node] = min(segTree[node * 2], segTree[node * 2 + 1]);

}

B.区间更新(线段树中最有用的)

需要用到延迟标记,每个节点新增加一个标记,用来记录这个节点是否被进行了某种修改操作(这种修改操作会影响其子节点),对于任意区间的修改,我们先按照查询的方式将其划分成线段树中的结点,然后修改这些节点中的信息,并给这些节点标上代表这种操作的标记,在修改和查询的时候,如果我们到了一个节点p,并且决定考虑其子节点,那我们就要看p有没有标记,如果有,就按照标记修改其子节点的信息,并且给子节点都标上相同的标记,同时取+消掉p的标记。

代码如下

void Change(node *p, int a, int b) /* 当前考察结点为p,修改区间为(a,b]*/

{

if (a <= p->Left && p->Right <= b)

/* 如果当前结点的区间包含在修改区间内*/

{

...... /* 修改当前结点的信息,并标上标记*/

return;

}

Push_Down(p); /* 把当前结点的标记向下传递*/

int mid = (p->Left + p->Right) / 2; /* 计算左右子结点的分隔点 */

if (a < mid) Change(p->Lch, a, b); /* 和左孩子有交集,考察左子结点*/

if (b > mid) Change(p->Rch, a, b); /* 和右孩子有交集,考察右子结点*/

Update(p); /* 维护当前结点的信息(因为其子结点的信息可能有更改)*/

}

线段树的主要运用:

1.区间最值查询问题:模板一;

2.连续区间修改或者单节点更新的动态查询问题:模板二;

3.多维空间的动态查询:模板三;

典型模板:

模板一:查询区间最值下标:

#include <iostream>

#include <string.h>

using namespace std;

#define maxind 256 //线段树节点个数

void build(int node,int b,int e,int m[],int a[])

{

if(b==e)

m[node]=b;//只有一个元素

else

{

build(2*node,b,(b+e)/2,m,a);

build(2*node+1,(b+e)/2+1,e,m,a);

if(a[m[2*node]]<=a[m[2*node+1]])

m[node]=m[2*node];

else

m[node]=m[2*node+1];

}

}

int query(int node,int b,int e,int m[],int a[],int i,int j)

{

int p1,p2;

if(i>e||j<b)

return -1;

if(b>=i&&e<=j)

return m[node];

p1=query(2*node,b,(b+e)/2,m,a,i,j);

p2=query(2*node+1,(b+e)/2+1,e,m,a,i,j);

if(p1==-1)

return m[node]=p2;

if(p2==-1)

return m[node]=p1;

if(a[p1]<=a[p2])

return m[node]=p1;

else

return m[node]=p2;

}

int main()

{

int l,r;//查询区间

scanf("%d%d",&l,&r);

int m[maxind];//下标从1起才有意义,否则不是二叉树

//保存下标编号结点对应区间最小值的下标

memset(m,-1,sizeof(m));

int a[]={3,4,5,7,2,1,0,3,4,5};

build(1,0,sizeof(a)/sizeof(a[0])-1,m,a);

cout<<query(1,0,sizeof(a)/sizeof(a[0])-1,m,a,l,r)<<endl;

return 0;

}

模板二:

#include<stdio.h>

#include<algorithm>

using namespace std;

const int maxm=111111;

long long add[maxm<<2];

long long sum[maxm<<2];

void pushup(int rt)

{

sum[rt]=sum[rt<<1]+sum[rt<<1|1];

}

void pushdown(int rt,int m)

{

if(add[rt])

{

add[rt<<1] += add[rt];

add[rt<<1|1] += add[rt];

sum[rt<<1] += add[rt] * (m - (m >> 1));

sum[rt<<1|1] += add[rt] * (m >> 1);

add[rt] = 0;

}

}

void build(int l,int r,int rt)

{

add[rt]=0;

if(l==r)

{

scanf("%lld",&sum[rt]);

return;

}

int m=(l+r)/2;

build(l,m,rt<<1);

build(m+1,r,rt<<1|1);

pushup(rt);

}

void update(int L,int R,int c,int l,int r,int rt) {

if (L <= l && r <= R) {

add[rt] += c;

sum[rt] += (long long)c * (r - l + 1);

return ;

}

pushdown(rt , r - l + 1);

int m = (l + r) >> 1;

if (L <= m) update(L , R , c , l,m,rt<<1);

if (m < R) update(L , R , c , m+1,r,rt<<1|1);

pushup(rt);

}

long long query(int L,int R,int l,int r,int rt) {

if (L <= l && r <= R) {

return sum[rt];

}

pushdown(rt , r - l + 1);

int m = (l + r) >> 1;

long long ret = 0;

if (L <= m) ret += query(L , R , l,m,rt<<1);

if (m < R) ret += query(L , R , m+1,r,rt<<1|1);

return ret;

}

int main()

{

int n,q;

scanf("%d%d",&n,&q);

build(1,n,1);

while(q--)

{

char op[2];

int a,b,c;

scanf("%s",op);

if(op[0]=='Q'){

scanf("%d%d",&a,&b);

printf("%lld\n",query(a,b,1,n,1));

}

else

{

scanf("%d%d%d",&a,&b,&c);

update(a,b,c,1,n,1);

}

}

return 0;

}

  

HDU 4893 Wow! Such Sequence!(2014年多校联合 第三场 G)(线段树)的更多相关文章

  1. hdu 4893 Wow! Such Sequence!(线段树)

    题目链接:hdu 4983 Wow! Such Sequence! 题目大意:就是三种操作 1 k d, 改动k的为值添加d 2 l r, 查询l到r的区间和 3 l r. 间l到r区间上的所以数变成 ...

  2. HDU 4893 Wow! Such Sequence! (线段树)

    Wow! Such Sequence! 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4893 Description Recently, Doge ...

  3. 线段树 + 区间更新: HDU 4893 Wow! Such Sequence!

    Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Oth ...

  4. HDU 4893 Wow! Such Sequence!(2014 Multi-University Training Contest 3)

    题意: 有三种操作: 1 x y: 表示给x位置加上y 2 x y:查询[x,y]的区间和 3 x y:将 [x,y] 区间上的数变为最接近的 Fibonacci. 思路: 1 操作按正常单调更新,区 ...

  5. hdu 4893 Wow! Such Sequence!

    http://acm.hdu.edu.cn/showproblem.php?pid=4893 三种操作: 1 k d - "add" 2 l r - "query sum ...

  6. HDU 4893 Wow! Such Sequence! (树状数组)

    题意:给有三种操作,一种是 1 k d,把第 k 个数加d,第二种是2 l r,查询区间 l, r的和,第三种是 3 l r,把区间 l,r 的所有数都变成离它最近的Fib数, 并且是最小的那个. 析 ...

  7. HDU 5762 Teacher Bo (鸽笼原理) 2016杭电多校联合第三场

    题目:传送门. 题意:平面上有n个点,问是否存在四个点 (A,B,C,D)(A<B,C<D,A≠CorB≠D)使得AB的横纵坐标差的绝对值的和等于CD的横纵坐标差的绝对值的和,n<1 ...

  8. HDU 5752 Sqrt Bo (思维题) 2016杭电多校联合第三场

    题目:传送门. 题意:一个很大的数n,最多开5次根号,问开几次根号可以得到1,如果5次还不能得到1就输出TAT. 题解:打表题,x1=1,x2=(x1+1)*(x1+1)-1,以此类推.x5是不超过l ...

  9. HDU 5754 Life Winner Bo (各种博弈) 2016杭电多校联合第三场

    题目:传送门 题意:一个国际象棋棋盘,有四种棋子,从(n,m)走到(1,1),走到(1,1)的人赢,先手赢输出B,后手赢输出G,平局输出D. 题解:先把从(n,m)走到(1,1)看做是从(1,1)走到 ...

随机推荐

  1. (Problem 14)Longest Collatz sequence

    The following iterative sequence is defined for the set of positive integers: n n/2 (n is even) n 3n ...

  2. 在DE1-SOC上运行Linux

    1,设定串口终端 安装驱动 :使用mini-USB线将计算机与DE1-SoC的UART转USB接口.drivers\USB2UART_driver文件夹内放置有驱动程序 设定串口终端规格 : 设定串口 ...

  3. MyEclipse8.5 中安装Spket插件

    MyEclipse8.5 中安装Spket插件 安装: 1. Myeclipse8.5安装和以前低版本有些不同(没有software updates). 点击help->Myeclipse Co ...

  4. docker学习笔记:修改无法启动的容器中的内容

    我们可能会碰到这样的一个问题,在容器执行过程中,修改了容器的内容(如配置文件信息),但因为修改出了问题.导致容器关闭后,无法启动. 这事需要重新修改配置文件. 正常情况下可以通过 docker exe ...

  5. (step7.2.4)hdu 2674(N!Again——简单数论)

    题目大意:输入一个整数n,输出N! mod 2009 的结果. 解题思路: 1)任意数  n = ( n / 2009) * 2009 + n % 2009 2)40!  mod 2009  等于 2 ...

  6. POJ 3422 Kaka&#39;s Matrix Travels (最小费用最大流)

    POJ 3422 Kaka's Matrix Travels 链接:http://poj.org/problem? id=3422 题意:有一个N*N的方格,每一个方格里面有一个数字.如今卡卡要从左上 ...

  7. sizeof与strlen的区别 浅谈

    1.sizeof operator sizeof是C语言的一种单目操作符,如C语言的其他操作符++.- - 等,它并不是函数. Queries size of the object or type. ...

  8. Bmp 解析 (2013-09-09 19:30:41)

    bmp 图片结构 位图格式(bmp)是显示图片的基本格式,其文件扩展名为*.BMP. 在Windows下,任何各式的图片文件(包括视频播放)都要转化为位图才能显示出来,其他各种格式的图片文件是在位图格 ...

  9. 高级UIKit-03(NSFileManager、NSFileHandle)

    fileManager文件管理器 [day04_1_FileManager_Search] :查找文件 fileManager有一个方法可以判断文件是否是文件夹, fileExistsAtPath:i ...

  10. C/C++迭代器使用具体解释

    迭代器是一种检查容器内元素并遍历元素的数据类型.能够替代下标訪问vector对象的元素. 每种容器类型都定义了自己的迭代器类型,如 vector: vector<int>::iterato ...