【LOJ2585】新家(APIO2018)-线段树+multiset
测试地址:新家
做法:本题需要用到线段树+multiset。
这题题意有点复杂,先理一下:数轴上有一些带颜色的点,每个点会在某个特定的时间段出现,若干个询问,每次询问某个时刻上离一个坐标距离最远的颜色的距离。坐标和颜色的距离定义为,该坐标与当前出现的所有该颜色的点的最小距离。
理完了题意,就可以想题了。首先因为没有强制在线,所以显然将操作按时间排序,而且把某个时间段出现某个点变成两个操作:某时刻新增一个点,又在某时刻将其删除。于是我们现在要想怎么找到最远的距离。
我们发现直接找到这个距离非常困难,因此想到二分答案,转化为判定性问题,即判定某个距离内存不存在所有的颜色。这时候我们要想到,点是可以随时修改的,所以肯定不能用主席树这种不能动的结构,那怎么办呢?我们给每一个点定义一个前驱,表示和它同颜色的,它左边离它最近的点。注意到,如果存在一个,使得,就表示点的颜色没有在区间内出现,所以我们只要求出内的最小值,再和比较,就可以知道区间内存不存在所有颜色了。
要注意的是,由于区间内出现的一个点有可能是该种颜色的最右侧的点,因此我们在数轴最右端插入一个点,这个点拥有全部的颜色,这样就可以处理这种情况了。于是我们只要对每种颜色开一个multiset存一下位置,以便找前驱和后继。
还有一个重要的问题,一个坐标上有可能有很多点,这时候我们光用坐标,颜色或者都无法区别它们,所以只能对每个离散化后的坐标位置存一个multiset来表示这个位置上的点集的所有值。而又因为这个问题,我们需要更严格地定义前驱和后继。一般认为,插入时,该点的后继的前驱就是插入后该点的前驱。这样的话,后继就是upper_bound,前驱就是upper_bound前面的一个。而在删除时略有不同,因为upper_bound的前面一个位置上是自己,所以还要再往前一个位置才是它真正的前驱。
于是我们用一堆multiset维护每个点的值,在涉及修改时,在线段树上单点修改,那么就可以完成一次答案的判定了。因此总的时间复杂度为。
当然,如果你想继续优化,方法还是有的,注意到目前时间复杂度的瓶颈在于二分答案再在线段树上询问,你可以小推一下,改成直接在线段树上二分,这样时间复杂度就变为了,但是我比较懒,就不写了,留作习题(喂喂喂…)。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
const int N=300010;
const int inf=100000010;
int n,k,q,tot,pos[N],ans[N];
int seg[N<<2];
struct oper
{
int v,x,t,type; //v:time,x:position,t:color,type:operation_type
}op[N<<2];
struct forsort
{
int id,val;
}f[N];
multiset<int> pointS[N],colorS[N];
multiset<int>::iterator it;
bool cmp(forsort a,forsort b)
{
return a.val<b.val;
}
bool cmpop(oper a,oper b)
{
if (a.v==b.v) return a.type<b.type;
else return a.v<b.v;
}
void init()
{
scanf("%d%d%d",&n,&k,&q);
for(int i=1;i<=n;i++)
{
int p,t,a,b;
scanf("%d%d%d%d",&p,&t,&a,&b);
int x=(i<<1)-1,y=(i<<1);
op[x].v=a,op[x].t=t,op[x].type=0;
op[y].v=b+1,op[y].t=t,op[y].type=1;
f[i].id=i;
f[i].val=p;
}
sort(f+1,f+n+1,cmp);
tot=-1;
for(int i=1;i<=n;i++)
{
if (i==1||f[i].val!=f[i-1].val)
pos[++tot]=f[i].val;
op[(f[i].id<<1)-1].x=tot;
op[f[i].id<<1].x=tot;
}
pos[++tot]=inf;
for(int i=1;i<=q;i++)
{
op[(n<<1)+i].type=2;
scanf("%d%d",&op[(n<<1)+i].x,&op[(n<<1)+i].v);
op[(n<<1)+i].t=i;
}
sort(op+1,op+(n<<1)+q+1,cmpop);
for(int i=0;i<tot;i++)
{
pointS[i].insert(inf);
//printf("insert %d %d\n",i,inf);
}
for(int i=1;i<=k;i++)
{
colorS[i].insert(-1),colorS[i].insert(tot);
pointS[tot].insert(-1);
}
}
void pushup(int no)
{
seg[no]=min(seg[no<<1],seg[no<<1|1]);
}
void modify(int no,int l,int r,int x,int d)
{
if (l==r) {seg[no]=d;return;}
int mid=(l+r)>>1;
if (x<=mid) modify(no<<1,l,mid,x,d);
else modify(no<<1|1,mid+1,r,x,d);
pushup(no);
}
int query(int no,int l,int r,int s,int t)
{
if (l>=s&&r<=t) return seg[no];
int mid=(l+r)>>1,ans=inf;
if (s<=mid) ans=min(ans,query(no<<1,l,mid,s,t));
if (t>mid) ans=min(ans,query(no<<1|1,mid+1,r,s,t));
return ans;
}
bool check(int l,int r)
{
int L,R,ans=inf;
L=lower_bound(pos,pos+tot+1,l)-pos;
if (L>tot) return 0;
R=upper_bound(pos,pos+tot+1,r)-pos;
R--;
if (R>=tot) R=tot-1;
if (L>R) return 0;
ans=min(ans,query(1,0,tot,R+1,tot));
return ans>=L;
}
void work()
{
for(int i=1;i<=((tot+1)<<2);i++)
seg[i]=inf;
int cnt=0;
for(int i=1;i<=(n<<1)+q;i++)
{
int p=op[i].x,t=op[i].t;
int pre,nxt;
if (op[i].type<2)
{
it=colorS[t].upper_bound(p);
nxt=(*it);it--;pre=(*it);
if (op[i].type==1&&pre==p) it--,pre=(*it);
}
if (op[i].type==0)
{
pointS[p].insert(pre);
it=pointS[nxt].find(pre);
pointS[nxt].erase(it);
//printf("insert %d %d\n",p,pre);
//printf("erase %d %d\n",nxt,pre);
pointS[nxt].insert(p);
//printf("insert %d %d\n",nxt,p);
modify(1,0,tot,p,(*pointS[p].begin()));
modify(1,0,tot,nxt,(*pointS[nxt].begin()));
if (colorS[t].size()==2) cnt++;
colorS[t].insert(p);
}
else if (op[i].type==1)
{
it=pointS[p].find(pre);
pointS[p].erase(it);
pointS[nxt].insert(pre);
//printf("erase %d %d\n",p,pre);
//printf("insert %d %d\n",nxt,pre);
it=pointS[nxt].find(p);
pointS[nxt].erase(it);
//printf("erase %d %d\n",nxt,p);
modify(1,0,tot,p,(*pointS[p].begin()));
modify(1,0,tot,nxt,(*pointS[nxt].begin()));
it=colorS[t].find(p);
colorS[t].erase(it);
if (colorS[t].size()==2) cnt--;
}
else
{
if (cnt<k) {ans[op[i].t]=-1;continue;}
int l=0,r=inf;
while(l<r)
{
int mid=(l+r)>>1;
if (check(op[i].x-mid,op[i].x+mid)) r=mid;
else l=mid+1;
}
ans[op[i].t]=l;
}
}
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
}
int main()
{
init();
work();
return 0;
}