zoukankan      html  css  js  c++  java
  • Evanyou Blog 彩带

    无聊的数列

    题目背景

    无聊的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:

    有没有巧妙的做法?


      分析:

      刚学完$zkw$线段树找道题来练练手。

      关于这题,如果直接上一般的区间修改+单点查询的话很难做,但是如果我们换个思路,运用差分的思想就会变得很容易了。

      我们把线段树维护的内容变成原数列的差分数列,也就是说$seg[i]=a[i]-a[i-1]$,那么单点查询的操作就变成了区间查询。但是修改操作呢?

      不难想到,因为是等差数列,所以差分数组的修改值实际上就是等差数列的公差。所以修改区间$[l,r]$时,先单点修改$l$,修改值为等差数列首项,再单点修改$r+1$,修改值为$-(K+(r-l)*D)$(自己想想为什么),最后再修改区间$[l+1,r]$,修改值为公差。就这样了。

      写的$zkw$线段树感觉还是常数有点大,总共跑了$335ms$,别人跑的快的只用$150ms$,不过至少比普通线段树快多了。

      Code:

    //It is made by HolseLee on 5th Sep 2018
    //Luogu.org P1438
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<iostream>
    #include<iomanip>
    #include<algorithm>
    using namespace std;
    
    typedef long long ll;
    const int N=1e5+7;
    int n,m,maxn;
    ll a[N],seg[N<<2],sign[N<<2];
    
    template<typename re>
    inline void read(re &x)
    {
        x=0; char ch=getchar(); bool flag=false;
        while( ch<'0' || ch>'9' ) {
            if( ch=='-' ) flag=true;
            ch=getchar();
        }
        while( ch>='0' && ch<='9' ) {
            x=(x<<1)+(x<<3)+ch-'0';
            ch=getchar();
        }
        flag ? x*=(-1) : 1;
    } 
    
    inline void modify(int pos,ll x)
    {
        for(pos+=maxn; pos; pos>>=1) seg[pos]+=x;
    }
    
    inline void update(int l,int r,ll x)
    {
        ll lum=0,rum=0,num=1;
        for(l=l+maxn-1,r=r+maxn+1; l^r^1; l>>=1,r>>=1,num<<=1) {
            seg[l]+=x*lum; 
            seg[r]+=x*rum;
            if( ~l&1 ) sign[l^1]+=x, seg[l^1]+=x*num, lum+=num;
            if( r&1 ) sign[r^1]+=x, seg[r^1]+=x*num, rum+=num;
        }
        for(; l; l>>=1,r>>=1) {
            seg[l]+=x*lum;
            seg[r]+=x*rum;
        }
    }
    
    inline ll quary(int l,int r)
    {
        ll ret=0,lum=0,rum=0,num=1;
        for(l=l+maxn-1,r=r+maxn+1; l^r^1; l>>=1,r>>=1,num<<=1) {
            if( sign[l] ) ret+=sign[l]*lum;
            if( sign[r] ) ret+=sign[r]*rum;
            if( ~l&1 ) ret+=seg[l^1], lum+=num;
            if( r&1 ) ret+=seg[r^1], rum+=num;
        }
        for(; l; l>>=1,r>>=1)
        ret+=sign[l]*lum, ret+=sign[r]*rum;
        return ret;
    }
    
    int main()
    {
        read(n); read(m); maxn=1;
        for(; maxn<=n+1; maxn<<=1);
        for(int i=1; i<=n; ++i) {
            read(a[i]); seg[maxn+i]=a[i]-a[i-1];
        }
        for(int i=maxn-1; i>=1; --i) seg[i]=seg[i<<1]+seg[i<<1|1];
        int opt,l,r; ll x,y;
        for(int i=1; i<=m; ++i) {
            read(opt);
            if( opt==1 ) {
                read(l), read(r), read(x), read(y);
                modify(l,x); modify(r+1,-(x+(ll)(r-l)*y));
                update(l+1,r,y);
            } else {
                read(x);
                printf("%lld
    ",quary(1,x));
            }
        }
        return 0;
    }
  • 相关阅读:
    java中eclipse控制台接受输入的方法
    java中Timer类的详细介绍(详解)
    java中Timer类的详细介绍(详解)
    java中Timer类的详细介绍(详解)
    java中Timer类的详细介绍(详解)
    java中ReentrantLock类的详细介绍(详解)
    java中ReentrantLock类的详细介绍(详解)
    java中ReentrantLock类的详细介绍(详解)
    java中ReentrantLock类的详细介绍(详解)
    Spring中WebApplicationInitializer的理解
  • 原文地址:https://www.cnblogs.com/cytus/p/9590071.html
Copyright © 2011-2022 走看看