zoukankan      html  css  js  c++  java
  • P1438 无聊的数列

    题目来源:洛谷

    题目背景

    无聊的YYB总喜欢搞出一些正常人无法搞出的东西。有一天,无聊的YYB想出了一道无聊的题:无聊的数列。。。(K峰:这题不是傻X题吗)

    题目描述

    维护一个数列{a[i]},支持两种操作:

    1、1 L R K D:给出一个长度等于R-L+1的等差数列,首项为K,公差为D,并将它对应加到a[L]~a[R]的每一个数上。即:令a[L]=a[L]+K,a[L+1]=a[L+1]+K+D,

    a[L+2]=a[L+2]+K+2D……a[R]=a[R]+K+(R-L)D。

    2、2 P:询问序列的第P个数的值a[P]。

    输入输出格式

    输入格式:

    第一行两个整数数n,m,表示数列长度和操作个数。

    第二行n个整数,第i个数表示a[i](i=1,2,3…,n)。

    接下来的m行,表示m个操作,有两种形式:

    1 L R K D

    2 P 字母意义见描述(L≤R)。

    输出格式:

    对于每个询问,输出答案,每个答案占一行。

    输入输出样例

    输入样例#1: 
    5 2
    1 2 3 4 5
    1 2 4 1 2
    2 3
    
    输出样例#1: 
    6

    说明

    数据规模:

    0≤n,m≤100000

    |a[i]|,|K|,|D|≤200

    Hint:

    有没有巧妙的做法?

    解析:

    这道题我跟一条题解想到一块去了。

    具体做法大概就是维护一条差分序列。为啥要维护差分序列呢?这个是可以从等差数列这个点看出来的。

    等差数列的性质决定了它可以用差分序列维护,这就保证了线段树的可结合性。

    跟不要说,题目写的很清楚的一则这个玩意:

    a[L]=a[L]+K,a[L+1]=a[L+1]+K+D,

    a[L+2]=a[L+2]+K+2D……a[R]=a[R]+K+(R-L)D。

    更是侧面应证了我们的思路的正确性。

    打几个草稿就能发现此差分数列维护规律:

    若差分序列为 d[],执行操作 1 l r k d

    1. d[l]=d[l]+k;

    2. d[i]=d[i]+d,l<i<=r;

    3. d[r+1]=d[r+1]-(k+d*(r-l));

    至于具体原因,详见差分序列性质,思考一下很容易明白。

    参考代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define N 100010
    #define ll long long
    using namespace std;
    ll a[N],b[N];
    struct segmentree{
        int l,r;
        ll dat,add;
    }t[N<<2];
    void build(int p,int l,int r)
    {
        t[p].l=l;t[p].r=r;
        if(l==r){
            t[p].dat=b[l];
            return;
        }
        int mid=(l+r)>>1;
        build(p<<1,l,mid);
        build(p<<1|1,mid+1,r);
        t[p].dat=t[p<<1].dat+t[p<<1|1].dat;
    }
    void pushdown(int p)
    {
        if(t[p].add)
        {
            t[p<<1].add+=t[p].add;
            t[p<<1|1].add+=t[p].add;
            t[p<<1].dat+=t[p].add*(t[p<<1].r-t[p<<1].l+1);
            t[p<<1|1].dat+=t[p].add*(t[p<<1|1].r-t[p<<1|1].l+1);
            t[p].add=0;
        }
    }
    void change(int p,int l,int r,ll val)
    {
        if(l<=t[p].l&&t[p].r<=r){
            t[p].add+=val;
            t[p].dat+=val*(t[p].r-t[p].l+1);
            return;
        }
        pushdown(p);
        int mid=(t[p].l+t[p].r)>>1;
        if(l<=mid) change(p<<1,l,r,val);
        if(r>mid) change(p<<1|1,l,r,val);
        t[p].dat=t[p<<1].dat+t[p<<1|1].dat;
    }
    ll query(int p,int l,int r)
    {
        if(l<=t[p].l&&t[p].r<=r) return t[p].dat;
        pushdown(p);
        int mid=(t[p].l+t[p].r)>>1;
        ll val=0;
        if(l<=mid) val+=query(p<<1,l,r);
        if(r>mid) val+=query(p<<1|1,l,r);
        return val;
    }
    int main()
    {
        int n,m,flag,l,r;
        ll k,d;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            b[i]=a[i]-a[i-1];//差分 
        }
        build(1,1,n);
        while(m--)
        {
            scanf("%d",&flag);
            if(flag==1){
                scanf("%d%d%lld%lld",&l,&r,&k,&d);
                //这个地方注意要特判
                //当需要修改的差分序列较特殊时,需要特殊处理 
                if(r==n&&l==r){
                    change(1,l,l,k);
                }
                else if(r>=n){
                    change(1,l,l,k);
                    change(1,l+1,r,d);
                }
                else{
                    change(1,l,l,k);
                    change(1,l+1,r,d);
                    change(1,r+1,r+1,-(k+d*(r-l)));//注意不用+1,由于差分序列改变的是l+1~r的值 
                }
            }
            else{
                scanf("%d",&r);
                printf("%lld
    ",query(1,1,r));
            }
        }
        return 0;
    }

    2019-05-17 14:23:49

  • 相关阅读:
    LA 6621 /ZOJ 3736 Pocket Cube 打表+暴力
    UVA 10273
    UVA 10158 并查集的经典应用
    CodeForces 382B 数学推导
    UVA 10806 最小费用最大流
    UVA 10330 最大流
    图论:匹配与覆盖+独立集 团与支配集
    sdut oj 操作系统实验--SSTF磁盘调度算法【操作系统算法】
    【转载】单调队列学习
    poj 3006 Dirichlet's Theorem on Arithmetic Progressions【素数问题】
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/10881105.html
Copyright © 2011-2022 走看看