zoukankan      html  css  js  c++  java
  • 洛谷P3246 [HNOI2016]序列

    https://www.luogu.com.cn/problem/P3246

    解法一:莫队+ST表+单调栈

    考虑如何由[L,R]的答案推向[L,R+1]的答案

    [L,R]向[L,R+1],增加的是[L,R+1]  [L+1,R+1] [L+2,R+1] …… [R,R+1] [R,R] 这些区间每个区间的最小值

    设[L,R+1]最小的数是a[p]

    那么区间左端点在[L,p]之间的这p-L+1个区间的最小值都是a[p]

    他们的贡献是a[p]*(p-L+1)

    还剩下区间左端点在[p+1,R+1]之间的区间

    设f[i]表示以i为右端点,[1,i]中的数为左端点的所有区间的贡献

    f[R+1]包含的是 [1,R+1] [2,R+1] [3,R+1]……[R,R+1] [R+1,R+1]

    f[p]包含的是[1,p] [2,p] [3,p] …… [p-1,p] [p,p]

    因为a[p]是区间[L,R+1]的最小值

    所以p<=R+1,且当i<=p时,[i,p]最小值等于[i,R+1]最小值  

    所以f[R+1]-f[p] 包含的是[p+1,R+1] [p+2,R+1] ……[R,R+1] [R+1,R+1],即我们需要得到的区间左端点在[p+1,R+1]之间的区间的贡献

    由[L,R]的答案推向[L,R-1]就根据上面算R的贡献减去即可

    移动左端点同理,f[i]就表示以i为左端点,[i,n]中的数为右端点的所有区间的贡献

    有一点要注意一下,我才用的莫队写法要先动右端点,涉及到区间查询,不允许出现L>R的情况

     

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define N 100003
    
    typedef long long LL;
    
    int S;
    int a[N];
    
    int l[N],r[N];
    int s[N],top;
    LL fl[N],fr[N]; 
    
    int lg2[N],st[N][17],stp[N][17];
    
    long long ans;
    
    int L=1,R;
    
    struct node
    {
        int l,r,id,pos;
        long long out;
    }e[N];
    
    bool cmp(node p,node q)
    {
        if(p.pos!=q.pos) return p.pos<q.pos;
        return p.r<q.r;
    }
    
    bool cmp2(node p,node q)
    {
        return p.id<q.id;
    }
    
    int query(int l,int r)
    {
        int len=lg2[r-l+1];
        if(st[l][len]<st[r-(1<<len)+1][len]) return stp[l][len];
        return stp[r-(1<<len)+1][len]; 
    }
    
    void updater(int pos,int ty)
    {
        int p=query(L,pos);
        long long tmp=1ll*(p-L+1)*a[p]+fl[pos]-fl[p];
        ans+=tmp*ty;
    }
    
    void updatel(int pos,int ty)
    {
        int p=query(pos,R);
        long long tmp=1ll*(R-p+1)*a[p]+fr[pos]-fr[p];
        ans+=tmp*ty;
    }
    
    
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        for(int i=2;i<=n;++i) lg2[i]=lg2[i>>1]+1;
        for(int i=1;i<=n;++i) 
        {
            st[i][0]=a[i];
            stp[i][0]=i;
        }
        for(int i=1;1<<i<=n;++i)
            for(int j=1;j+(1<<i)-1<=n;++j)
            {
                st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]);
                if(st[j][i]==st[j][i-1]) stp[j][i]=stp[j][i-1];
                else stp[j][i]=stp[j+(1<<i-1)][i-1];
            }
        a[0]=-2e9;
        for(int i=1;i<=n;++i)
        {
            while(a[i]<=a[s[top]]) top--;
            l[i]=s[top];
            fl[i]=fl[l[i]]+1ll*(i-l[i])*a[i];
            s[++top]=i;
        }
        a[n+1]=-2e9;
        s[top=0]=n+1;
        for(int i=n;i;--i)
        {
            while(a[i]<=a[s[top]]) top--;
            r[i]=s[top];
            fr[i]=fr[r[i]]+1ll*(r[i]-i)*a[i];
            s[++top]=i;
        }
        S=sqrt(n);
        for(int i=1;i<=m;++i) 
        {
            scanf("%d%d",&e[i].l,&e[i].r);
            e[i].id=i;
            e[i].pos=(e[i].l-1)/S+1;
        }
        sort(e+1,e+m+1,cmp);
        for(int i=1;i<=m;++i)
        {
            while(R<e[i].r) updater(++R,1);
            while(R>e[i].r) updater(R--,-1);
            while(L<e[i].l) updatel(L++,-1);
            while(L>e[i].l) updatel(--L,1);    
            e[i].out=ans;
        }
        sort(e+1,e+m+1,cmp2);
        for(int i=1;i<=m;++i) printf("%lld
    ",e[i].out);
    }
    View Code

    解法二:在线做法

    与上面莫队算法类似思路

    设区间[L,R]最小值是a[p]

    可以把答案分为3部分

    一部分是左端点在[L,p]且右端点在[p,R]的区间,这些区间的最小值都是a[p],贡献是(p-L+1)*(R-p+1)*a[p]

    第二部分是左右端点都在[p+1,R]的区间

    设f[i]表示以i为右端点,[1,i]中的数为左端点的所有区间的贡献

    当右端点是k时,贡献是f[k]-f[p]

    这一点在上面的莫队算法中已经说明

    所以当右端点在[p+1,R]时,贡献是f[p+1]-f[p]+f[p+2]-f[p]……+f[R-1]-f[p]+f[R]-f[p]

    令g表示f的前缀和,贡献就是g[R]-g[p]-f[p]*(R-p)

    第三部分是左右端点都在[L,p-1]的区间

    同理

    设f[i]表示以i为左端点,[i,n]中的数为右端点的所有区间的贡献

    g表示f的后缀和

    贡献是g[L]-g[p]-f[p]*(p-L)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define N 100003
    
    typedef long long LL;
    
    int a[N];
    
    int l[N],r[N];
    int s[N],top;
    LL fl[N],fr[N],gl[N],gr[N]; 
    
    int lg2[N],st[N][17],stp[N][17];
    
    int query(int l,int r)
    {
        int len=lg2[r-l+1];
        if(st[l][len]<st[r-(1<<len)+1][len]) return stp[l][len];
        return stp[r-(1<<len)+1][len]; 
    }
    
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        for(int i=2;i<=n;++i) lg2[i]=lg2[i>>1]+1;
        for(int i=1;i<=n;++i) 
        {
            st[i][0]=a[i];
            stp[i][0]=i;
        }
        for(int i=1;1<<i<=n;++i)
            for(int j=1;j+(1<<i)-1<=n;++j)
            {
                st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]);
                if(st[j][i]==st[j][i-1]) stp[j][i]=stp[j][i-1];
                else stp[j][i]=stp[j+(1<<i-1)][i-1];
            }
        a[0]=-2e9;
        for(int i=1;i<=n;++i)
        {
            while(a[i]<=a[s[top]]) top--;
            l[i]=s[top];
            fl[i]=fl[l[i]]+1ll*(i-l[i])*a[i];
            gl[i]=gl[i-1]+fl[i];
            s[++top]=i;
        }
        a[n+1]=-2e9;
        s[top=0]=n+1;
        for(int i=n;i;--i)
        {
            while(a[i]<=a[s[top]]) top--;
            r[i]=s[top];
            fr[i]=fr[r[i]]+1ll*(r[i]-i)*a[i];
            gr[i]=gr[i+1]+fr[i];
            s[++top]=i;
        }
        int l,r,p;
        long long ans;
        for(int i=1;i<=m;++i) 
        {
            scanf("%d%d",&l,&r);
            p=query(l,r);
            ans=1ll*(p-l+1)*(r-p+1)*a[p];
            ans+=gr[l]-gr[p]-1ll*fr[p]*(p-l);
            ans+=gl[r]-gl[p]-1ll*fl[p]*(r-p);
            printf("%lld
    ",ans);
        }
    }
    View Code
    作者:xxy
    本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
  • 相关阅读:
    今天碰到的angular 中的一个小坑
    mvc 防止客服端多次提交
    自定义通用Distinct去除重复数据的2中方式
    Sql 字符串操作类COALESCE
    SQL Server 性能优化
    Visual Studio Tip: Get Public Key Token for a Strong Named Assembly
    C#发送邮件
    Web打印组件jatoolsPrinter(转载)
    SQL SERVER 2005 同步复制技术(转)
    [Asp.net]常见word,excel,ppt,pdf在线预览方案(转)
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/15150757.html
Copyright © 2011-2022 走看看