漏网之鱼

题目描述

给定一个 $n$ 个数的序列 $a$ ,有 $q$ 次询问,每次询问区间 $[l,r]$ 的所有子区间的 $mex$ 的和。

数据范围

$1 \le n,Q \le 10^6$ ; $0 \le a_i \le 10^9$

题解

考虑把 $[l,r]$ 的答案拆成起点在 $[1,r]$ ,终点在 $[l,r]$ 的答案减去起点在 $[1,l-1]$ 的答案。

考虑离线,考虑推动l指针,线段树上端点 $r$ 维护 $[l,r]$ 的 $mex$ 及历史信息。考虑 $l$ 到 $l+1$ 的时候的贡献,画个图会发现它会把一段区间改成 $a_l$ 。

现在考虑终点在 $r$ ,如何维护起点在 $[1,l]$ 的总答案,考虑每次 $mex$ 在变化的时候,原来 $mex$ 的贡献就是 $(l-last) \times mex$ ,将式子拆开,就是 $l \times mex-last \times mex$ ,于是可以用线段树维护 $mex$ 的和和以前历史的总和减去现在 $mex \times last$ 的值,查询时带 $l$ 下去查询即可。

效率: $O(nlogn)$

代码

#include <bits/stdc++.h>
#define LL long long
#define _(d) while(d(isdigit(c=getchar())))
using namespace std;
int Rd(){
    char c;_(!);int x=c^48;
    _()x=(x<<3)+(x<<1)+(c^48);return x;
}
const int N=1e6+5,M=N<<2;
int Ty,n,nx[N],lst[N],a[N],q,b[N],ax[M],g[M][2];
LL ans[N],s[M][2],tg[M][2];
bool vis[N];
struct O{
    int l,r,i,x;
}p[N*2];
bool cmp(O A,O B){return A.x<B.x;}
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
void down(int k,int l,int r){
    if (!tg[k][0] && !tg[k][1]) return;
    s[k][0]+=tg[k][0]*(r-l+1);
    s[k][1]+=tg[k][1]*(r-l+1);
    if (l<r){
        tg[Ls][0]+=tg[k][0];tg[Ls][1]+=tg[k][1];
        tg[Rs][0]+=tg[k][0];tg[Rs][1]+=tg[k][1];
    }
    tg[k][0]=tg[k][1]=0;
}
void up(int k,int l,int r){
    s[k][0]=s[Ls][0]+(mid-l+1)*tg[Ls][0]+s[Rs][0]+(r-mid)*tg[Rs][0];
    s[k][1]=s[Ls][1]+(mid-l+1)*tg[Ls][1]+s[Rs][1]+(r-mid)*tg[Rs][1];
    g[k][0]=max(g[Ls][0],g[Rs][0]);
    g[k][1]=(g[Ls][1] || g[Rs][1] || g[Ls][0]!=g[Rs][0]);
}
void build(int k,int l,int r){
    if (l==r){g[k][0]=s[k][0]=b[l];return;}
    build(Ls,l,mid);build(Rs,mid+1,r);up(k,l,r);
}
LL qry(int k,int l,int r,int L,int R,int i){
    down(k,l,r);
    if (L<=l && r<=R) return s[k][0]*i+s[k][1];
    if (mid>=R) return qry(Ls,l,mid,L,R,i);
    if (mid<L) return qry(Rs,mid+1,r,L,R,i);
    return qry(Ls,l,mid,L,R,i)+qry(Rs,mid+1,r,L,R,i);
}
void upd(int k,int l,int r,int L,int R,int i,int v){
    if (l<r && !g[k][1]) g[Ls][0]=g[Rs][0]=g[k][0];
    down(k,l,r);
    if (L<=l && r<=R && !g[k][1]){
        tg[k][0]+=v-g[k][0];
        tg[k][1]-=1ll*(v-g[k][0])*(i-1);
        g[k][0]=v; down(k,l,r); return;
    }
    if (mid>=L && g[Ls][0]>v) upd(Ls,l,mid,L,R,i,v);
    if (mid<R && g[Rs][0]>v) upd(Rs,mid+1,r,L,R,i,v);
    up(k,l,r);
}
int main(){
    Ty=Rd();n=Rd();
    for (int i=1;i<=n;i++){
        a[i]=min(Rd(),n);
        nx[lst[a[i]]]=i,lst[a[i]]=i;
    }
    q=Rd();
    for (int j=0,l,r,i=1;i<=q;i++){
        l=Rd(),r=Rd();
        p[++j]=(O){l,r,i,l-1};
        p[++j]=(O){l,r,i,r};
    }
    sort(p+1,p+q+q+1,cmp);
    for (int i=1;i<=n;i++){
        vis[a[i]]=1;b[i]=b[i-1];
        while(vis[b[i]]) b[i]++;
        if (!nx[i]) nx[i]=n+1;
    }
    build(1,1,n); int j=1; while(!p[j].x) j++;
    for (int i=1;i<=n;i++){
        while(p[j].x==i)
            ans[p[j].i]+=(p[j].x<p[j].l?-1:1)*
            qry(1,1,n,p[j].l,p[j].r,i),j++;
        upd(1,1,n,i,nx[i]-1,i+1,a[i]);
    }
    for (int i=1;i<=q;i++)
        printf("%lld\n",ans[i]);
    return 0;
}
posted @ 2019-12-16 08:04  xjqxjq  阅读(285)  评论(0编辑  收藏  举报