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\) 时:

(废话?预警)哦那么扫描线有多少条呢?首先一个矩形对应两条线,所以空间自带两倍常熟。二类贡献显然是每个 \(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;
}
浙公网安备 33010602011771号