HDU_4893 2014多校三 线段树

给定一个初始都为0的序列,有三种操作,前两种比较正常,一个是对某个位置的数add k,另一个是query区间和。然后比较麻烦的是第三个操作,把某个区间里面的每个值改成离它最近的Fibonacci数,如果存在左右两个离它近的,优先取左边数值小的

一看到前两个操作马上就想上手敲树状数组,后来看到第三个就有点傻眼了,思维当时一直停留在怎么快速改值。。但忽略了题目本身要求什么,只有操作2才是输出,也就是只要求区间和值而且,其他两个都是操作而已,在聪哥的提醒下,知道对线段树开两个值记录+一个懒惰标记,一个值d记录正常的区间和值,另一个sf记录当前的fibanacci数值和

懒惰标记记录当前区间是否被3操作过,若被标记了,下次query就是返回sf,否则返回d。

不过一开始思路还是有点不清晰,还WA了几发。明显按上面的转化就是一个普通的单点更新+懒惰标记+区间查询的线段树了嘛

主要错在pushdown函数和3操作处理上。没分清模块

首先3操作命名为fix,fix找到对应的区间后,就进行懒惰标记,顺便把当前区间d改为sf值,然后向上传递。

add操作,首先,找到最后单点即进行更新操作,否则pushdown,把懒惰标记传递下去,在标记里面也是要对d=sf操作的,最后向上传递结果

query就更明显了,找区间return sf 或者 d,没找到就先pushdown

一开始糊里糊涂,pushdown又没push好,还没修改当前的值

比赛的时候还是有点糊里糊涂,线段树功底还不够,还要继续练

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define LL __int64
using namespace std;
const int N = 100010;
LL f[100];
LL d[N<<2],flag[N<<2],sf[N<<2];
LL pref[N];
int n,q;
void init()
{
    LL tmp=1LL<<60;
    f[0]=f[1]=1;
    for (int i=2;i<=90;i++){
        f[i]=f[i-1]+f[i-2];

    }
}

void build(int rt ,int l,int r)
{
    flag[rt]=d[rt]=0;
    if (l>=r){
        sf[rt]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    sf[rt]=sf[rt<<1]+sf[rt<<1|1];
}
void change(int rt)
{
    d[rt]=sf[rt];
}
void up(int rt,int l,int r)
{
    d[rt]=d[rt<<1]+d[rt<<1|1];
    sf[rt]=sf[rt<<1]+sf[rt<<1|1];

}
void pushdown(int rt,int l,int r)
{
    int mid=(l+r)>>1;
    if (flag[rt]==0 || l>=r) return;
    flag[rt<<1]=flag[rt<<1|1]=flag[rt];
    d[rt<<1]=sf[rt<<1];
    d[rt<<1|1]=sf[rt<<1|1];
    flag[rt]=0;
}
void fix(int L,int R,int rt,int l,int r)
{
    if (flag[rt]==1) return;
    if (L<=l && r<=R){
        flag[rt]=1;
        d[rt]=sf[rt];
        return;
    }
    int mid=(l+r)>>1;
    if (R<=mid) fix(L,R,lson);
    else
    if (L>mid) fix(L,R,rson);
    else{
        fix(L,R,lson);
        fix(L,R,rson);
    }
    if (flag[rt<<1]==1 && flag[rt<<1|1]==1){
        flag[rt]=1;
    }
    up(rt,l,r);
}
void add(int loc,LL val,int rt,int l,int r)
{
    if (l>=r)
    {
        flag[rt]=0;
        d[rt]+=val;
        int loc=lower_bound(f,f+90,d[rt])-f;
        sf[rt]=f[loc];
        if (loc>0){
            if (d[rt]-f[loc-1]<=f[loc]-d[rt]){
            sf[rt]=f[loc-1];
            }
        }
        return;
    }
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    if (loc<=mid) add(loc,val,lson);
    else add(loc,val,rson);
    up(rt,l,r);
}

LL query(int L,int R,int rt,int l,int r)
{
    if (L<=l && r<=R){
        if (flag[rt]) return sf[rt];
        else return d[rt];
    }
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    if (R<=mid) return query(L,R,lson);
    else
    if (L>mid) return query(L,R,rson);
    else
    {
        LL ret1=query(L,R,lson);
        LL ret2=query(L,R,rson);
        return ret1+ret2;
    }
}
int main()
{
    init();
    int op;
    while (scanf("%d%d",&n,&q)!=EOF)
    {
        build(1,1,n);
        while (q--)
        {
            scanf("%d",&op);
            if (op==1){
                int a;
                LL b;
                scanf("%d%I64d",&a,&b);
                add(a,b,1,1,n);
            }
            else
            if (op==2){
                int a,b;
                scanf("%d%d",&a,&b);
                LL ans=query(a,b,1,1,n);
                printf("%I64d\n",ans);
            }
            else{
                int a,b;
                scanf("%d%d",&a,&b);
                fix(a,b,1,1,n);
            }
        }
    }
    return 0;
}

HDU_4893 2014多校三 线段树,布布扣,bubuko.com

时间: 2024-08-05 01:27:01

HDU_4893 2014多校三 线段树的相关文章

HDU 4960 Handling the past 2014 多校9 线段树

首先确定的基本思想是按时间离散化后来建线段树,对于每个操作插入到相应的时间点上 但是难就难在那个pop操作,我之前对pop操作的处理是找到离他最近的那个点删掉,但是这样对于后面的peak操作,如果时间戳还在pop前面,那就需要还原之前的pop操作,这弄得很不方便 于是就有了一种类似扫描线的做法,对于push操作+1,pop操作-1,对于每次peak操作,从他这点往左走,找到第一个>0的点即可 #include <iostream> #include <cstdio> #inc

HDU 4902 Nice boat 多校4 线段树

给定n个数 第一个操作和普通,区间覆盖性的,把l-r区间的所有值改成固定的val 第二个操作是重点,输入l r x 把l-r区间的所有大于x的数,变成gcd(a[i],x) a[i]即指满足条件的序列上的数值 最后才输出所有值 当时苦思这个地方如何优化,想着不可能单点去更新吧,但是区间gcd,不能保存下来,一来他是要>x才有效,本来聪哥想了一种先把各种x gcd一遍,最后在push下去,发现不行,就是因为他对>x才有效,你在区间就直接gcd了,那不是把不该gcd的也给搞了 还想过说先存起来所有

HDU 5023 (2014广州网络赛 线段树)

解题思路: 有两种操作,第一种是将区间 a 到 b 染成颜色 c ,第二种是询问区间 a  到 b 内的颜色,按照顺序输出. 注意到颜色只有三十种,因此我们可以用二进制数表示颜色,通过位运算来操作. #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <cmath> #include <vector> #include

hdu 2795 线段树--点更新

http://acm.hdu.edu.cn/showproblem.php?pid=2795 多校的第一场和第三场都出现了线段树,比赛期间没做,,这两天先做几道热身下,然后31号之前把那两道多校的线段树都搞了,这是一道热身题 关键是建模: 首先一定看清楚题目构造的场景,看有什么特点--------会发现,如果尽量往左上放置的话,那么由于 the i-th announcement is a rectangle of size 1 * wi.,完全可以对h建立线段树,表示一行,结点里的l,r就表示

POJ 2886 Who Gets the Most Candies(线段树+约瑟夫环)

题目链接:POJ 2886 Who Gets the Most Candies [题目]N个孩子顺时针坐成一个圆圈,从1~N编号,每个孩子手中有一张标有非零整数的卡片.第K个孩子先出圈,如果他手中卡片上的数字A>0,下一个出圈的是他左手边第A个孩子.A<0,下一个出圈的是他右手边第(-A)个孩子.第p个出圈的孩子会得到F(p)个糖果,F(p)为p的因子数.输出得到糖果数最多的孩子的名字及糖果数目. [思路]孩子数目很大(1~500000),于是想到要用线段树来优化,然后就是模拟出圈过程.并更新

POJ 3468 线段树+lazy标记

lazy标记 Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u Submit Status Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to

线段树详解及模板 (转载)

一步一步理解线段树 目录 一.概述 二.从一个例子理解线段树 创建线段树 线段树区间查询 单节点更新 区间更新 三.线段树实战 -------------------------- 一 概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn). 线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)

一步一步理解线段树——转载自JustDoIT

一步一步理解线段树 目录 一.概述 二.从一个例子理解线段树 创建线段树 线段树区间查询 单节点更新 区间更新 三.线段树实战 -------------------------- 一 概述 线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn). 线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间

【转载】一步一步理解线段树

转载自:http://www.cnblogs.com/TenosDoIt/p/3453089.html 目录 一.概述 二.从一个例子理解线段树 创建线段树 线段树区间查询 单节点更新 区间更新 三.线段树实战 -------------------------- 一 概述 线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn). 线段树的每个节点表示一个区间,子节点则分别表示父