zoukankan      html  css  js  c++  java
  • 【BZOJ1558】等差数列(JSOI2009)-差分+线段树

    测试地址:等差数列
    做法:本题需要用到差分+线段树。
    我们发现等差数列这个东西非常难维护,于是我们将序列和修改全部差分,我们发现一个有趣的性质:等差数列的差分中各个数相等(其实是句废话)。最关键的是,我们把玄乎的修改操作变成了用三个区间加操作就能完成:从s1s的差分增加了a,从ii+1(si<t)的差分增加了b,从tt+1的差分增加了a(ts)b
    有了这样的转化,我们就可以用线段树维护差分序列了,那么看似玄乎的询问和这个差分序列之间有什么关系呢?前面已经说了,等差数列的差分中各个数相等,很多同学就会想,答案是不是就是差分序列中不同数字的段数?然而这个直觉是错的,因为在划分原序列时,我们实际上并不是在将差分序列划分成段,而是把差分序列中某些数删掉,使得剩下的数字中,不同的数字之间不相邻。删掉一个差分序列中的数,就是在原序列中加入一个断点,所以总的段数应该是最小的断点数+1
    那我们怎么找到最小的断点数呢?注意到,每有一对相邻的不同数字出现,那么这两个数字中就要至少删掉一个。如果整个差分序列都是长度大于等于2的连续段拼在一起的,那么每个选择间互不影响,最小断点数就是出现这种情况的数对数。现在的问题是,有些连续段长度为1,那么就会有一些选择是相互影响的,于是我们应该把会产生影响的数对合在一起考虑,如果有x个这样的数对连在一起,要满足这些数对的要求需要删掉x+12个数。到了这一步,我们考虑怎么样合并区间的信息,我们要维护区间左端和右端的数(为了判断是不是和另一端接的数字相等),区间左端和右端长度为1的连续段数量(或者和上面一样直接维护数对数,都是为了计算拼起来之后的最优解),整个区间的最优解(即最小断点数)。合并两个区间时,如果左边区间的右端点和右边区间的左端点数字相同,那么两个区间内的断点选择互不影响,直接相加即可。否则,左边区间和右边区间拼起来后,中间产生了新的一段长度为1的连续段,需要重新计算这一段选择的最优解。
    于是我们就解决了这一题,时间复杂度为O(nlogn)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll inf=1000000000ll*1000000000ll;
    int n,q,lft[400010],rht[400010],ans[400010],nowr,nowans;
    ll v[100010],lftv[400010],rhtv[400010],tag[400010],nowrv;
    
    int Ans(int x)
    {
        return (x+1)>>1;
    }
    
    void pushdown(int no)
    {
        if (tag[no]!=0)
        {
            tag[no<<1]+=tag[no];
            tag[no<<1|1]+=tag[no];
            lftv[no<<1]+=tag[no],rhtv[no<<1]+=tag[no];
            lftv[no<<1|1]+=tag[no],rhtv[no<<1|1]+=tag[no];
            tag[no]=0;
        }
    }
    
    void pushup(int no,int l,int r)
    {
        int mid=(l+r)>>1;
        if (rhtv[no<<1]!=lftv[no<<1|1])
        {
            ans[no]=ans[no<<1]+ans[no<<1|1];
            ans[no]-=Ans(rht[no<<1])+Ans(lft[no<<1|1]);
            ans[no]+=Ans(rht[no<<1]+lft[no<<1|1]+1);
            if (lft[no<<1]==mid-l)
                lft[no]=lft[no<<1]+lft[no<<1|1]+1;
            else lft[no]=lft[no<<1];
            if (rht[no<<1|1]==r-mid-1)
                rht[no]=rht[no<<1|1]+rht[no<<1]+1;
            else rht[no]=rht[no<<1|1];
        }
        else
        {
            ans[no]=ans[no<<1]+ans[no<<1|1];
            lft[no]=lft[no<<1];
            rht[no]=rht[no<<1|1];
        }
        lftv[no]=lftv[no<<1];
        rhtv[no]=rhtv[no<<1|1];
    }
    
    void buildtree(int no,int l,int r)
    {
        tag[no]=0;
        if (l>r) return;
        if (l==r)
        {
            lft[no]=rht[no]=ans[no]=0;
            lftv[no]=rhtv[no]=v[l];
            return;
        }
        int mid=(l+r)>>1;
        buildtree(no<<1,l,mid);
        buildtree(no<<1|1,mid+1,r);
        pushup(no,l,r);
    }
    
    void add(int no,int l,int r,int s,int t,ll d)
    {
        if (s>t||s<1||t>n-1) return;
        if (l>=s&&r<=t)
        {
            tag[no]+=d;
            lftv[no]+=d;
            rhtv[no]+=d;
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (s<=mid) add(no<<1,l,mid,s,t,d);
        if (t>mid) add(no<<1|1,mid+1,r,s,t,d);
        pushup(no,l,r);
    }
    
    void query(int no,int l,int r,int s,int t)
    {
        if (s>t) return;
        if (l>=s&&r<=t)
        {
            if (nowrv!=inf&&nowrv!=lftv[no])
            {
                nowans+=ans[no];
                nowans-=Ans(nowr)+Ans(lft[no]);
                nowans+=Ans(nowr+lft[no]+1);
                if (rht[no]==r-l) nowr=nowr+rht[no]+1;
                else nowr=rht[no];
            }
            else
            {
                nowans+=ans[no];
                nowr=rht[no];
            }
            nowrv=rhtv[no];
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (s<=mid) query(no<<1,l,mid,s,t);
        if (t>mid) query(no<<1|1,mid+1,r,s,t);
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&v[i]);
            v[i-1]=v[i]-v[i-1];
        }
    
        buildtree(1,1,n-1);
        scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            char op[3];
            int s,t;
            ll a,b;
            scanf("%s",&op);
            if (op[0]=='A')
            {
                scanf("%d%d%lld%lld",&s,&t,&a,&b);
                add(1,1,n-1,s-1,s-1,a);
                add(1,1,n-1,s,t-1,b);
                add(1,1,n-1,t,t,-a-(ll)(t-s)*b);
            }
            else
            {
                scanf("%d%d",&s,&t);
                nowr=0;
                nowrv=inf;
                nowans=0;
                query(1,1,n-1,s,t-1);
                printf("%d
    ",nowans+1);
            }
        }
    
        return 0;
    }
  • 相关阅读:
    一致Hash算法
    Ubuntu14.04上深度学习Caffe库安装指南(CUDA7.5 + opencv3.1)
    springcloud 服务调用的两种方式
    Spring Boot学习记录(二)–thymeleaf模板
    Springmvc构造RESTful详细讲解
    pring MVC过滤器-HttpPutFormContentFilter
    Spring MVC中各个filter的用法
    Spring MVC 使用拦截器 HiddenHttpMethodFilter配置Rest风格的URL
    springmvc 入门(1)
    oracle sql语言模糊查询--通配符like的使用教程
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793361.html
Copyright © 2011-2022 走看看