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;
    }
  • 相关阅读:
    ASP.NET中FileUpload中的代码怎么编写?
    JQuery EasyUI 根据数据动态生成datagrid,统计常用
    JQuery EasyUI window 用法
    jQuery EasyUI DataGrid 分页 FOR ASP.NET
    SQL server 2008 不允许保存更改,您所做的更改要求删除并重新创建以下表 的解决办法
    javascript 判断浏览器客户端
    Access, SQL Server, and Oracle数据类型的对应关系
    asp.net中的模态对话框
    [转]C++获取Windows时间的方法总结
    [转]Oracle开发与使用文章收藏
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793361.html
Copyright © 2011-2022 走看看