Cowcatcher Practice Round 34 F little w and Discretization (Persistable Line Trees)
猛然发现,过这道题竟然已经是10天前的事情了……
题意大概就是说给你一个序列,每次询问把一个区间的数字离散化,问离散化之后与原本数字不相同的数字个数有多少个。这里有很多个询问,每个询问之间相互独立。
既然是要离散化,我们肯定不能到询问的时候再离散化,肯定是在做所有询问之前,先自己离散化一下。我们考虑一个数字离散化之后与它本身的区别,由于这里说了数列里面的数字都是1~1e9的,所以一个数字离散化之后,肯定是小于等于它本身的。我们要求的是一个区间内,离散化之后与原本数字不相同的个数,我们可以考虑反过来可以求与原本数字相同的个数。对于一个数字,如果在整个区间所有数字离散化情况下为x,那么只离散化一个小区间的时候,数字肯定是小于等于x的。所以说,对于整体离散化之后,如果某个数字已经小于它本身了,那么这个数字就可以不再考虑。
略掉那些直接可以不考虑的数字之后,我们看看剩下的数字怎么处理。观察后可以发现,如果在一个区间内,某一个数字x已经比它离散化之后的数字大了,那么所有大于x的数字一定都会比它对应离散化之后的数字大。那么,我们的问题就转化为了,求一个区间里面最大的一个数字,使得其离散化之后的数字与其本身相同。再一分析,我们可以发现,相当于求一个区间的mex,也即从1开始最大的没有出现过的整数。找到这个mex之后,我们还需要知道这个区间里面有多少个小于等于mex的数字,用区间长度减去这个数字就是我们最后的答案。
那么,现在问题就是如何求区间mex和区间小于等于mex的数字个数。我们考虑用可持久化线段树,对于每一个子线段树,我保存前i个数字离散化后的属性。每一个棵树维护区间[1,n]表示到目前位置每个数字最后晚出现的时间,没有出现则为0,然后维护区间最小值。对于询问(l,r),我在第r棵线段树里面找到一个小于l的最小的位置,这个位置就是我们想要的mex。然后就是求小于等于mex的数字个数,这个也是一样,在可持久化线段树里面多维护一个数字个数即可。
这题在比赛的时候已经想出,但是有一个小地方没有注意到。我们在初始的时候,就可以略去一些一定不会与离散化后数字相等的数字,这些数字不需要加入可持久化线段树,但是线段树的继承信息不能因为这个不加入而断开。这一点我疏忽了,所以GG……具体见代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 300100;
int n,m,num[N],a[N],b[N];
int rt[N<<4]; //空间一定要开大,不要吝啬
struct Persistent_SegTree
{
struct node{int l,r,min,sum;} T[N<<5];
int cnt; void init(){cnt=0;}
void ins(int &i,int old,int l,int r,int x,int y)
{
T[i=++cnt]=T[old];
int mid=(l+r)>>1; T[i].sum++;
if (l==r) {T[i].min=y;return;}
if (x<=mid) ins(T[i].l,T[old].l,l,mid,x,y);
else ins(T[i].r,T[old].r,mid+1,r,x,y);
T[i].min=min(T[T[i].l].min,T[T[i].r].min);
}
int query(int i,int l,int r,int x)
{
if (l==r) return l;
int mid=(l+r)>>1;
if (T[T[i].l].min<x) return query(T[i].l,l,mid,x);
else return query(T[i].r,mid+1,r,x);
}
int getsum(int i,int j,int l,int r,int L,int R)
{
//cout<<L<<' '<<R<<endl;
if (l==L&&R==r) return T[j].sum-T[i].sum;
int mid=(L+R)>>1;
if (r<=mid) return getsum(T[i].l,T[j].l,l,r,L,mid);
else if (l>mid) return getsum(T[i].r,T[j].r,l,r,mid+1,R);
else return getsum(T[i].l,T[j].l,l,mid,L,mid)+getsum(T[i].r,T[j].r,mid+1,r,mid+1,R);
}
} Persist;
int main()
{
scanf("%d",&n);
Persist.init();
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
num[i]=b[i]=a[i];
}
sort(num+1,num+1+n);
int tot=unique(num+1,num+1+n)-num-1;
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(num+1,num+1+tot,a[i])-num;
if (a[i]==b[i]) Persist.ins(rt[i],rt[i-1],1,tot,a[i],i);
else rt[i]=rt[i-1]; //这个继承一定不能断开
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int l,r;
scanf("%d%d",&l,&r);
int num=Persist.query(rt[r],1,tot,l);
//cout<<num<<endl;
int t=Persist.getsum(rt[l-1],rt[r],1,num,1,tot);
printf("%d\n",r-l+1-t);
}
return 0;
}
上一篇: 数据结构作业 11 - 二叉树(多选)
下一篇: 面试男神女神?华为人工智能工程师职位探索