• BZOJ4540 Hnoi2016 序列 【莫队+RMQ+单调栈预处理】*


    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

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

    Sample Input

    5 5
    5 2 4 1 3
    1 5
    1 3
    2 4
    3 5
    2 5

    Sample Output

    28
    17
    11
    11
    17

    HINT

    1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9


    先考虑把一段区间向左边延伸一个点
    我们发现在这个新区间里,额外的贡献就是以l为左端点,l+1~r所有节点为右节点的贡献,然后我们显然是可以用前缀和处理以i为左端点,i+1~n所有点为右节点的答案。但是我们发现前缀和的信息只能从上一个比当前节点小的节点转化而来,又因为在这个区间里,区间的最小值向右的部分中答案就是区间最小值,所以我们ST表预处理一下,找到区间最小值,然后剩下的部分就用前缀和处理就好了
    然后在处理前缀和的时候需要正反处理,用单调栈维护一下就好了

    然后一定要注意莫队运算的顺序!!!!


    #include<bits/stdc++.h>
    using namespace std;
    #define N 100010
    #define LL long long
    #define pi pair<LL,LL>
    struct Node{LL l,r,id;}p[N];
    LL n,m,w[N],lg2[N];
    LL block[N],siz;
    pi ST[N][20];
    LL s[N],top;
    LL sum1[N],sum2[N],ans[N],res=0;
    bool cmp(Node a,Node b){
        if(block[a.l]==block[b.l])return a.r<b.r;
        return a.l<b.l;
    }
    pi compare(pi a,pi b){return (a.first<b.first)?a:b;}
    void STpre(){
        lg2[0]=-1;for(int i=1;i<=n;i++)lg2[i]=lg2[i>>1]+1;
        for(LL i=1;i<=n;i++)ST[i][0]=(pi){w[i],i};
        for(LL k=1;k<=18;k++)
            for(LL i=1;i+(1<<k)-1<=n;i++)
                ST[i][k]=compare(ST[i][k-1],ST[i+(1<<k-1)][k-1]);
    }
    LL query(LL l,LL r){
        LL k=lg2[r-l+1];
        return compare(ST[l][k],ST[r-(1<<k)+1][k]).second;
    }
    void init(){
        s[top=0]=0;
        for(LL i=1;i<=n;i++){
            while(top&&w[i]<=w[s[top]])top--;
            sum1[i]=sum1[s[top]]+(i-s[top])*w[i];
            s[++top]=i;
        }
        s[top=0]=n+1;
        for(LL i=n;i>=1;i--){
            while(top&&w[i]<=w[s[top]])top--;
            sum2[i]=sum2[s[top]]+(s[top]-i)*w[i];
            s[++top]=i;
        }
    }
    LL queryL(int l,int r){
        int tmp=query(l,r);
        return (r-tmp+1)*w[tmp]+sum2[l]-sum2[tmp];
    }
    LL queryR(int l,int r){
        int tmp=query(l,r);
        return (tmp-l+1)*w[tmp]+sum1[r]-sum1[tmp];
    }
    int main(){
        scanf("%lld%lld",&n,&m);
        for(LL i=1;i<=n;i++)scanf("%lld",&w[i]);
        for(LL i=1;i<=m;i++)scanf("%lld%lld",&p[i].l,&p[i].r),p[i].id=i;
        siz=sqrt(n);
        for(LL i=1;i<=n;i++)block[i]=(i-1)/siz+1;
        sort(p+1,p+m+1,cmp);
        STpre();
        init();
        int l=1,r=0;
        for(LL i=1;i<=m;i++){
            while(r<p[i].r)res+=queryR(l,++r);
            while(l>p[i].l)res+=queryL(--l,r);
            while(r>p[i].r)res-=queryR(l,r--);
            while(l<p[i].l)res-=queryL(l++,r);
            ans[p[i].id]=res;
        }
        for(LL i=1;i<=m;i++)printf("%lld
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    杨中科 向HtmlAgilityPack道歉:解析HTML还是你好用
    感觉这个JQuery不错,查询方便
    数据库异步操作
    Command 设计模式
    osg 细节裁剪 SAMLL_FEATURE_CULLING
    errno错误代码
    清空std::stringstream
    eclipse android javabuilder +CDTbuilder
    mfc c++ system调用 控制台窗口
    Androidndkr8e wordlist 第二个参数不是数值参数
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676312.html
Copyright © 2020-2023  润新知