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\n",ans[i]);
    return 0;
}

 

 

 

posted @ 2017-07-26 21:11  Troywar  阅读(242)  评论(0编辑  收藏  举报