4540: [Hnoi2016]序列|莫队+ST表

考虑现在已经知道了 [l,r] 的答案新添入一个 r+1 如何更新答案
也就是右端点在 r+1 处左端点在 l..r+1 之间的所有的子序列的答案
可以找出 l..r 中最小的数的位置 p ,然后 p 以及 p 左侧作为左端点的答案就可以直接计算了
考虑左端点在 p+1....r+1 时对答案的贡献,可以与处理一个前缀和 Si 表示以 i 为右端点的所有子序列的答案之和
那么左端点在 p+1....r+1 时对答案的贡献就是 Sr+1Sp
其他端点移动的做法也同理
为什么我的莫队跑了17s,而网上的其他莫队只需要5s,人傻自带三倍常数QWQ

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define N 110000
#define ll long long
using namespace std;
int sc()
{
    int i=0,f=1; char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i*f;
}
struct W{int l,r,p;} b[N];
long long sl[N],sr[N],ans[N],now;
int bl[N],l[N],r[N],a[N],st[N][17],q[N];
int n,Q,block;
void pre()
{
    for(int i=1;i<=n;i++) st[i][0]=i;
    for(int k=1;(1<for(int i=1;i<=n;i++)
            if(i+(1<n+1)break;
            else if(a[st[i][k-1]]1<1)][k-1]]) st[i][k]=st[i][k-1];
            else st[i][k]=st[i+(1<1)][k-1];
    int qr=0;
    for(int i=1;i<=n;i++)
    {
        while(qr&&a[q[qr]]>=a[i])qr--;
        l[i]=q[qr]; q[++qr]=i;
        sl[i]=sl[l[i]]+1ll*a[i]*(i-l[i]);
    }
    q[qr=0]=n+1;
    for(int i=n;i>=1;i--)
    {
        while(qr&&a[q[qr]]>a[i])qr--;
        r[i]=q[qr]; q[++qr]=i;
        sr[i]=sr[r[i]]+1ll*a[i]*(r[i]-i);
    }
}
bool cmp(W a,W b)
{
    return bl[a.l]int get_min(int l,int r)
{
    int k=log2(r-l+1);
    int L=st[l][k],R=st[r-(1<1][k];
    return a[L]int l,int r)
{
    int p=get_min(l,r);
    return 1ll*(r-p+1)*a[p]+sr[l]-sr[p];
}
long long calr(int l,int r)
{
    int p=get_min(l,r);
    return 1ll*(p-l+1)*a[p]+sl[r]-sl[p];
}
int main()
{
    n=sc(),Q=sc();block=sqrt(n);
    for(int i=1;i<=n;i++)
        a[i]=sc(),bl[i]=(i-1)/block;
    pre();
    for(int i=1;i<=Q;i++)
        b[i].l=sc(),b[i].r=sc(),b[i].p=i;
    sort(b+1,b+Q+1,cmp);
    int L=1,R=0;
    for(int i=1;i<=Q;i++)
    {
        while(L>b[i].l)now+=call(L-1,R),L--;
        while(R1),R++;
        while(Lwhile(R>b[i].r)now-=calr(L,R),R--;
        ans[b[i].p]=now;
    }
    for(int i=1;i<=Q;i++)
        printf("%lld\n",ans[i]);
    return 0;
}

你可能感兴趣的:(莫队算法,BZOJ刷题记录)