UNV637 【美团杯2021 A】数据结构

题意简介

给定一个长度为 \(n\) 的序列 \(a\),序列的标号从 \(1\) 开始,每一个位置是一个 \([1,n]\) 内的整数。有 \(m\) 个操作,每次操作给出一个区间 \([l,r]\):这次操作会先将下标在
\([l,r]\) 中的所有元素加上 \(1\),然后询问全局颜色数,并在最后撤销这次修改(也就是说操作之间互不影响)。

做法分析

问的是颜色数,也即考虑每一种数值出现与否。考虑每种数什么时候出现或不出现。一个数 \(x\) 在一次询问做出贡献,要么原序列最左与最右的 \(x\) 的下标构成的 \(l,r\) 被询问区间包含,要么原序列存在值为 \(x-1\) 的元素下标被询问区间包含。上述条件反过来就是做不出贡献的情况。

由于这是CNDS考虑把询问离线下来,贡献加减变成矩形做扫描线。具体来说,这个扫描线长这个样子。我们记 \(tot\) 为所有可达的数字(也就是说,那些无法在任何询问中出现的数字我们忽略不记),把每个数字做不出贡献的 \(l,r\) 范围框定出来。

具体怎么框定?对于只能由 \(x-1\) 加上来的数,其做不出贡献的 \(l,r\) 对形如一个个 \(l\in[x_i,x_i+t],r\in[x_i,x_i+t]\)。(记为一类)(也即下图中 \(3,6,8\) 的样子)

除此之外,其做不出贡献的区间形如 \(l\in(ll,rl],r\in[lr,rr)\),其中 \(lr,rl\) 分别为序列中最小和最大的的值为 \(x\) 的下标,\(ll,rr\) 分别为 \(lr\) 左侧最靠近 \(lr\) 的和 \(rl\) 右侧最靠近 \(rl\) 的,值为 \(x-1\) 的下标。(记为二类)

特殊地,当存在 \(x-1\) 位于两个 \(x\) 之间时,\(x\) 永远不会消失,因此特判掉,其不会带来任何扫描线。
(为什么这里维护做不出贡献的区间?因为区间数量少,空间复杂度有保证。)

举个例子:当序列为 \(2\,1\,2\,4\,7\,5\,7\,4\) 时:
pEAUF0A.png

(废话?预警)哦那么扫描线有多少条呢?首先一个矩形对应两条线,所以空间自带两倍常熟。二类贡献显然是每个 \(O(1)\) 的。一类呢?观察到对于这样的每个 \(x\)\(x-1\) 出现 \(k\) 次时,会有 \(k+1\) 个正方形(不一定取满)。粗略地看,若总共有 \(y\) 个这样的 \(x\),每个 \(x\) 出现 \(k_i\) 次,可以认为空间就会占 \(\sum_{i=1}^{y} k_i+1=\sum k_i+y\)。又 \(\sum k_i\) 可以认为是个定值,所以 \(y\) 越多时空间占的就越多(说大白话就是每种 \(x-1\) 只出现一次,每个正方形就出现两次,这个过程还要重复 \(y\) 次,差不多等于说是两倍空间了)。

所以简简简而言之就是:存线段数组开四倍空间。

代码实现

找那啥二类的 \(ll,rr\) 等等时,为防止 \(ll,rr\) 不存在,往 \(apr\) 里面塞 \(0\)\(n+1\)
如果 tmp1 找到了 N+1要特殊处理。

#include <bits/stdc++.h>
using namespace std;
namespace obasic{
    typedef vector<int> vecint;
    template <typename _T>
    void frdi(_T &x){
        _T k=1;x=0;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
        x*=k;return;
    }
    template <typename _T>
    void fwri(_T x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)fwri(x/10);
        putchar(x%10+'0');
    }
    template <typename _T>
    int lwberv(vector<_T> &vec,_T val){return *lower_bound(vec.begin(),vec.end(),val);}
};
using namespace obasic;
const int MaxN=1e6+5;
int N,M,A[MaxN],pre[MaxN],pap[MaxN];
int acnt[MaxN],tot,ans[MaxN];
struct quer{int x,y,id;}Q[MaxN];
bool cmp1(quer a,quer b){return a.x<b.x;}
vecint apr[MaxN];int scnt;
struct aseg{int x,ly,ry,v;}S[MaxN<<2];
void addrect(int lx,int rx,int ly,int ry){
    S[++scnt]={lx,ly,ry,1};
    S[++scnt]={rx+1,ly,ry,-1};
}
bool cmp2(aseg a,aseg b){return a.x<b.x;}
struct BinidTree{
    int n,t[MaxN];
    void init(int x){n=x,memset(t,0,sizeof(t));}
    int lowbit(int x){return x&(-x);}
    void add(int p,int x){for(;p<=n;p+=lowbit(p))t[p]+=x;}
    void update(int l,int r,int x){add(l,x),add(r+1,-x);}
    int gts(int p){int res=0;for(;p;p-=lowbit(p)){res+=t[p];}return res;}
}BidTr;
int main(){
    frdi(N),frdi(M);BidTr.init(N+1),tot=N+1;
    for(int i=0;i<=N+1;i++)apr[i].push_back(0);
    for(int i=1;i<=N;i++){
        frdi(A[i]);apr[A[i]].push_back(i);
        acnt[A[i]]++;pre[i]=pap[A[i]],pap[A[i]]=i;
    }
    for(int i=0;i<=N+1;i++)apr[i].push_back(N+1);
    int tmp1,tmp2,clx,crx,cly,cry;
    for(int i=1;i<=N+1;i++){
        int cc=acnt[i],pc=acnt[i-1];
        if(!cc){
            if(!pc){tot--;continue;}
            for(int j=1,p,q=0;j<=pc+1;j++){
                p=q,q=apr[i-1][j];
                if(q==p+1)continue;
                addrect(p+1,q-1,p+1,q-1);
            }
            continue;
        }
        tmp1=lwberv(apr[i-1],apr[i][1]);
        if(tmp1<apr[i][acnt[i]])continue;
        tmp2=lwberv(apr[i-1],apr[i][cc]);
        crx=apr[i][1],cly=apr[i][acnt[i]];
        clx=(tmp1==N+1?apr[i-1][pc]:pre[tmp1])+1,cry=tmp2-1;
        addrect(clx,crx,cly,cry);
        continue;
    }
    sort(S+1,S+scnt+1,cmp2);
    for(int i=1;i<=M;i++)frdi(Q[i].x),frdi(Q[i].y),Q[i].id=i;
    sort(Q+1,Q+M+1,cmp1);for(int i=1,p=1,q=1;i<=N+1;i++){
        for(;p<=scnt&&S[p].x==i;p++)BidTr.update(S[p].ly,S[p].ry,S[p].v);
        for(;q<=M&&Q[q].x==i;q++)ans[Q[q].id]=tot-BidTr.gts(Q[q].y);
    }
    for(int i=1;i<=M;i++)fwri(ans[i]),puts("");
    return 0;
}
posted @ 2025-01-22 09:05  矞龙OrinLoong  阅读(70)  评论(0)    收藏  举报