HDU4893 线段树 修改为最近的Fib

传送门:点击打开链接

Wow! Such Sequence!

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 3392    Accepted Submission(s): 981


Problem Description
Recently, Doge got a funny birthday present from his new friend, Protein Tiger from St. Beeze College. No, not cactuses. It's a mysterious blackbox.

After some research, Doge found that the box is maintaining a sequence an of n numbers internally, initially all numbers are zero, and there are THREE "operations":

1.Add d to the k-th number of the sequence.
2.Query the sum of ai where l ≤ i ≤ r.
3.Change ai to the nearest Fibonacci number, where l ≤ i ≤ r.
4.Play sound "Chee-rio!", a bit useless.

Let F 0 = 1,F 1 = 1,Fibonacci number Fn is defined as F n = F n - 1 + F n - 2 for n ≥ 2.

Nearest Fibonacci number of number x means the smallest Fn where |F n - x| is also smallest.

Doge doesn't believe the machine could respond each request in less than 10ms. Help Doge figure out the reason.
 

Input
Input contains several test cases, please process till EOF.
For each test case, there will be one line containing two integers n, m.
Next m lines, each line indicates a query:

1 k d - "add"
2 l r - "query sum"
3 l r - "change to nearest Fibonacci"

1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000, |d| < 2 31, all queries will be valid.
 

Output
For each Type 2 ("query sum") operation, output one line containing an integer represent the answer of this query.
 

Sample Input
  
  
1 1 2 1 1 5 4 1 1 7 1 3 17 3 2 4 2 1 5
 

Sample Output
  
  
0 22
 

Author
Fudan University
 

Source


题意:

1 把第K个点加上D;

2 把区间[l,r]里的数变为离它最近的斐波那契数(这个fib数最小,且这个fib减这个数的绝对值也最小)

3 询问[l,r]区间内的和

思路:

维护两个sum。sum1存放当前区间的和,sum2存放当前区间所有数变成fib之后的和。这样,进行覆盖的时候,被覆盖的区间直接sum1=sum2就行了。

详细操作见代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define LL __int64
struct node
{
    int l,r;
    LL sum1,sum2;
    bool c;
}tree[400005];
LL F[105];
void pushup(int id)
{
    tree[id].sum1=tree[id<<1].sum1+tree[id<<1|1].sum1;
    tree[id].sum2=tree[id<<1].sum2+tree[id<<1|1].sum2;
}
void pushdown(int id)
{
    if(tree[id].c)
    {
        tree[id<<1].c=tree[id<<1|1].c=1;
        tree[id<<1].sum1=tree[id<<1].sum2;
        tree[id<<1|1].sum1=tree[id<<1|1].sum2;
        tree[id].c=0;
    }
}
void build(int id,int l,int r)
{
    tree[id].l=l;
    tree[id].r=r;
    tree[id].c=0;
    if(l==r)
    {
        tree[id].sum1=0;
        tree[id].sum2=1;
    }
    else
    {
        int mid=(l+r)>>1;
        build(id<<1,l,mid);
        build(id<<1|1,mid+1,r);
        pushup(id);
    }
}
LL near(LL x)
{
    int p=lower_bound(F,F+90,x)-F;
    if(p&&abs(x-F[p-1])<=abs(F[p]-x)) return F[p-1];
    else return F[p];
}
void add(int id,int pos,int x)
{
    if(tree[id].l==tree[id].r)
    {
        tree[id].sum1+=x;
        tree[id].sum2=near(tree[id].sum1);
    }
    else
    {
        pushdown(id);
        int mid=(tree[id].l+tree[id].r)>>1;
        if(pos<=mid) add(id<<1,pos,x);
        else add(id<<1|1,pos,x);
        pushup(id);
    }
}
void op(int id,int l,int r)
{
    if(l<=tree[id].l&&tree[id].r<=r)
    {
        tree[id].sum1=tree[id].sum2;
        tree[id].c=1;
    }
    else
    {
        pushdown(id);
        int mid=(tree[id].l+tree[id].r)>>1;
        if(l<=mid) op(id<<1,l,r);
        if(mid<r) op(id<<1|1,l,r);
        pushup(id);
    }
}
LL sum(int id,int l,int r)
{
    if(l<=tree[id].l&&tree[id].r<=r) return tree[id].sum1;
    else
    {
        pushdown(id);
        int mid=(tree[id].l+tree[id].r)>>1;
        LL res=0;
        if(l<=mid) res+=sum(id<<1,l,r);
        if(mid<r) res+=sum(id<<1|1,l,r);
        return res;
    }
}
int main()
{
    F[0]=1;
    F[1]=1;
    for(int i=2;i<=90;i++) F[i]=F[i-1]+F[i-2];
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        build(1,1,n);
        while(m--)
        {
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            if(x==1) add(1,y,z);
            else if(x==2)
                printf("%I64d\n",sum(1,y,z));
            else op(1,y,z);
        }
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值