• LG P3246 [HNOI2016]序列


    Description

    给定长度为$n$的序列:$a_1,a_2,cdots ,a_n$,记为$a[1:n]$。类似地,$a[l:r](1 leq lleq rleq N)$是指序列:$a_l,a_{l+1},cdots,a_{r-1},a_r$。若$1leq lleq sleq tleq rleq n$,则称$a[s:t]$是$a[l:r]$的子序列。现在有$q$个询问,每个询问给定两个数$l$和$r$,$1leq lleq rleq 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$。

    Solution

    对于离线的做法,可以使用莫队,考虑从区间$[l,r]$转移至区间$[]l,r+1]$时对答案产生的贡献

    这些贡献是以$r+1$为右端点的,左端点在$[l,r+1]$的所有区间最小值之和

    设$p$是区间$[l,r+1]$的最小值位置,那么最小值在$p$的答案可以提前算出

    设$pre_i$为$i$之前第一个比$a_i$小的数的位置,可以单调栈预处理,设$f_{i}$表示左端点在$[1,i]$,右端点在$r+1$的区间最小值之和,那么

    $$f_{i}=a_i(a_i-pre_i)+f_{pre_i}$$

    $f_i-f_p$就是剩余的对答案的贡献

    对于在线的做法,发现$f_i$也可以代表右端点在$i$,左端点在$[1,i]$的区间最小值之和

    对于区间$[l,r]$,先求出$p$,对于$p$右侧的部分,答案即为$f_{p+1}+cdots + f_{r-1}+f_r-(r-p)f_p$,可以使用前缀和$O(1)$求出

    对于左侧同理

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    int n,q,lg[100005]={-1},minn[100005][21],sta[100005],top,suf[100005],pre[100005];
    long long fr[100005],fl[100005],gr[100005],gl[100005],a[100005];
    inline int read(){
        int f=1,w=0;
        char ch=0;
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
        return f*w;
    }
    int main(){
        n=read(),q=read(),a[0]=a[n+1]=0x7f7f7f7f;
        for(int i=1;i<=n;i++)a[i]=read(),lg[i]=lg[i>>1]+1,minn[i][0]=i;
        for(int i=1;i<=20;i++)for(int j=1;j+(1<<i)-1<=n;j++)minn[j][i]=a[minn[j][i-1]]<a[minn[j+(1<<(i-1))][i-1]]?minn[j][i-1]:minn[j+(1<<(i-1))][i-1];
        for(int i=1;i<=n;i++){
            while(top&&a[sta[top]]>a[i])suf[sta[top--]]=i;
            pre[i]=sta[top],sta[++top]=i;
        }
        while(top)suf[sta[top--]]=n+1;
        for(int i=1;i<=n;i++)fr[i]=fr[pre[i]]+a[i]*(i-pre[i]),gr[i]=gr[i-1]+fr[i];
        for(int i=n;i;i--)fl[i]=fl[suf[i]]+a[i]*(suf[i]-i),gl[i]=gl[i+1]+fl[i];
        for(;q;q--){
            int l=read(),r=read(),p=a[minn[l][lg[r-l+1]]]<a[minn[r-(1<<lg[r-l+1])+1][lg[r-l+1]]]?minn[l][lg[r-l+1]]:minn[r-(1<<lg[r-l+1])+1][lg[r-l+1]];
            printf("%lld
    ",a[p]*(p-l+1)*(r-p+1)+gr[r]-gr[p]-fr[p]*(r-p)+gl[l]-gl[p]-fl[p]*(p-l));
        }
        return 0;
    }
    [HNOI2016]序列
  • 相关阅读:
    [APIO2018]铁人两项 --- 圆方树
    SPOJ1811 && SPOJ1812
    [BZOJ4627][BeiJing2016]回转寿司(线段树)
    NOIP2018提高组题解
    [BZOJ4340][BJOI2015]隐身术(后缀数组)
    [BZOJ4338][BJOI2015]糖果(扩展Lucas)
    [BZOJ4336][BJOI2015]骑士的旅行(树链剖分+线段树)
    [BZOJ4419][SHOI2013]发微博
    [BZOJ2878][NOI2012]迷失游乐园(环套树DP+概率)
    [BZOJ1791][IOI2008]Island岛屿(环套树DP)
  • 原文地址:https://www.cnblogs.com/JDFZ-ZZ/p/14534373.html
Copyright © 2020-2023  润新知