• bzoj4540: [Hnoi2016]序列


    Description

      给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
    1
    ,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
    ≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
    6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。

    Input

      输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。接下来一行,包含n个整数,以空格隔开
    ,第i个整数为ai,即序列第i个元素的值。接下来q行,每行包含两个整数l和r,代表一次询问。

    Output

      对于每次询问,输出一行,代表询问的答案。

    题解:

      参照于网上某位大佬。。。-----http://www.cnblogs.com/ccz181078/p/6227639.html
      对于每个a[i],若其在[l,r]有贡献,满足l<=i,r>=i,,a[l...i]>=a[i],a[i...r]>=a[i],然后我们可以预处理出每个a[i]统辖的l,r的位置。然后对于每个询问L,R,包含子序列l,r满足L<=l<=r<=R,转化一下,就是在一个平面中求L<=x<=R,L<=y<=R这个矩形面积(不满足前一个式子的可以不加)。这个时候就可以用二维树状数组或者线段树O(n(logn)^2)乱搞了。但时间还是不优,考虑优化。把询问离线,按照L从大到小排序。令与y轴平行的扫描线沿x轴负方向扫描,把符合当前条件的修改添加。

      然后就是喜闻乐见的差分了=-=。(调了4个小时。。。绝望TAT)对于每个三元组(l,op,r)l,r为op极大区域内满足op最小的左右端点。(比如2、4、5、1,对于op==2,满足l==2,r==3)那么考虑对每次询问的贡献,对于某个三元组,在扫描线扫的时候,我们把贡献分成进入离开该三元组,将贡献进行差分。(此处不太好理解。)当我们进入该区间时,我们直接先将其在平面上对应的矩形面积拓展到坐标轴上,离开时,再将之前多覆盖的减去。当计算答案时(还是2、4、5、1,如询问L=3,R=4时,我们显然看到op==4的左端点小于3),我们需要再次差分,因为我添加时是把op~r添加的,对于答案的贡献就是min(op-i[当前询问],op-L[op])*(r[op]-op)*a[op],又一开始时设置了将面积添加改变为两部分,min(op-L[当前询问],op-l[op])直接就可以改为op-L[当前询问],分成两个树状数组,对于第一个数组,记录需要减去的a[op],第二个直接记录全部贡献。最后统计时再给第一个乘上L[当前询问]。对于每个树状数组,其实都是假二维,因为我们一个点是影响一个矩形的取值,所以在y轴方向上直接乘上x的大小,时间就优化了下来。

      这个可能解释得不大清楚。。。(我真的尽力了。。。)如果您有疑问(且愿意)可以参照代码理解。。。(毕竟我这么弱。。。。)

    #include<cstdio>
    #include<algorithm>
    typedef long long ll;
    const int N=100100;
    int n,q,a[N];
    int que[N],top,l[N],r[N];
    ll ans[N];
    int read(){
        int x=0,f=1;
        char ch=getchar();
        while(ch<48||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>47&&ch<='9')x=x*10+ch-48,ch=getchar();
        return x*f;
    }
    ll c1[2][N],c2[2][N];
    void add(ll*a1,ll*a2,int x,ll w){
        if(!x)return;
        for(int i=x;i;i-=i&-i)a1[i]+=w;
        w*=x;
        for(int i=x;i<=n;i+=i&-i)a2[i]+=w;
    }
    ll sum(ll*a1,ll*a2,int x){
        if(!x)return 0;
        ll s=0;
        for(int i=x;i<=n;i+=i&-i)s+=a1[i];
        s*=x;
        for(int i=x-1;i;i-=i&-i)s+=a2[i];
        return s;
    }
    struct Query{
        int l,r,id;
        void get_ans(){
            ans[id]=sum(c1[0],c1[1],r)*l+sum(c2[0],c2[1],r);
        }
        friend bool operator<(Query a,Query b){return a.l>b.l;}
    }query[N];
    
    struct P{
        int l,r1,r2;
        ll k,b;
        void push(){
            add(c1[0],c1[1],r2,k),add(c1[0],c1[1],r1-1,-k);
            add(c2[0],c2[1],r2,b),add(c2[0],c2[1],r1-1,-b);
        }
        friend bool operator<(P a,P b){return a.l>b.l;}
    }e[N*2];
    
    int ep=0;
    int main(){
      //  freopen("sequence2.in","r",stdin);
      //  freopen("sequence.out","w",stdout);
        n=read();q=read();
        for(int i=1;i<=n;++i)a[i]=read();
        for(int i=0;i<q;++i)
            query[i].l=read(),query[i].r=read(),query[i].id=i;
        std::sort(query,query+q);
        for(int i=1;i<=n;++i){
            while(top&&a[que[top]]>a[i])r[que[top--]]=i-1;
            que[++top]=i;
        }
        while(top)r[que[top--]]=n;
        for(int i=n;i;--i){
            while(top&&a[que[top]]>=a[i])l[que[top--]]=i+1;
            que[++top]=i;
        }
        while(top)l[que[top--]]=1;
        for(int i=1;i<=n;++i){
            e[ep++]=(P){i,i,r[i],-a[i],ll(i+1)*a[i]};
            e[ep++]=(P){l[i],i,r[i],a[i],ll(-l[i])*a[i]};
        }
        std::sort(e,e+ep);
        for(int i=0,p=0;i<q;++i){
            while(p<ep&&e[p].l>=query[i].l)e[p++].push();
            query[i].get_ans();
        }
        for(int i=0;i<q;++i)printf("%lld
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    第四次作业—— 分析比较各种软件构建环境
    如何实现点击事件触发之后刷新还保存原值
    简单理解js闭包
    javascript中 __proto__与prorotype的理解
    原生和jQuery的ajax用法
    getElementById和querySelector方法的区别
    关于javascript闭包理解
    第二篇 进销存管理系统冲刺博客
    个人项目:WC
    自我介绍+软工五问
  • 原文地址:https://www.cnblogs.com/Troywar/p/7241742.html
Copyright © 2020-2023  润新知