题解:luogu P9000([CEOI 2022] Measures)
1. Description
有 \(N\) 个站在数轴上的人,他们的初始位置分别为 \(a_1,a_2,\ldots,a_N\),他们可以以 \(1\) 个单位长度每秒的速度移动。
因为众所周知的原因,他们需要保持社交距离,也就是说在任两个人之间距离至少为 \(D\)。
Alenka 设计了一个 app 来快速求出这 \(N\) 个人通过移动来保持社交距离的最小时间,现在她想要添加一个新功能:支持动态加入一个位置为 \(b_i\) 的人。
你需要实现一个程序完成这个功能。
2. Solution
首先我们发现答案可以二分,所以先考虑二分。
我们把所有人的位置从小到大排序之后从最左边的人开始枚举,则我们会尝试让这个人尽可能地向左走,换句话说,如果假设二分出的答案为 \(x\),每个人最后到达的位置为 \(f_i\),则 \(f_i=\max(f_{i-1}+D,a_i-x)\),当 \(\exists i,f_i-a_i>x\) 时,\(x\) 不合法。
我们尝试展开这个东西,\(f_i=\max_{j=1}^i[a_j-x+(i-j)D]\),则不合法条件为 \(\max_{j=1}^i[a_j-x+(i-j)D]-a_i>x\),进一步的,也即 \(\max_{j=1}^i (a_j-jD)-(a_i-iD)>2x\)。
利用线段树维护上面这个值,很容易做到 \(O(n\log n)\)。
具体的,在线段树的每个节点中,维护 \(a_i-iD\) 的最大值和最小值,以及 \(\max_{j=1}^i (a_j-jD)-(a_i-iD)\) 的值,则合并时,只需要先合并两边的答案,然后再用右边的最大值减去左边的最小值即可。
注意,加入一个点时,它的 \(i\) 是此时的排名,所以我们利用一个数据结构维护已经加入的数,同时,线段树也要在对应区间中减 \(D\)。
3. Code
/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int N=2e5+15;
const ll inf=4e18;
int n,m,D,tot;
int a[N],rk[N];
pii tmp[N];
struct Node{
ll mi,mx,ans;
Node(ll _mi=0,ll _mx=0,ll _ans=0){
mi=_mi,mx=_mx,ans=_ans;
}
Node operator +(const Node &T)const{
return Node(min(mi,T.mi),max(mx,T.mx),max({ans,T.ans,mx-T.mi}));
}
Node& operator +=(const ll &T){
mi+=T,mx+=T;
return *this;
}
};
struct Binary_tree{
int c[N];
#define lowbit(x) (x&-x)
void add(int x,int v){
for(int i=x;i<=n+m;i+=lowbit(i))
c[i]+=v;
}
int query(int x){
int res=0;
for(int i=x;i;i-=lowbit(i))
res+=c[i];
return res;
}
#undef lowbit
}bit;
struct Segment_tree{
Node c[N<<2];
ll tag[N<<2];
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
void pushup(int p){
c[p]=c[ls]+c[rs];
}
void Tag(int p,ll v){
tag[p]+=v;
c[p]+=v;
}
void pushdown(int p){
Tag(ls,tag[p]);
Tag(rs,tag[p]);
tag[p]=0;
}
void build(int p,int l,int r){
tag[p]=0,c[p]=Node(inf,-inf,0);
if(l==r)return ;
build(ls,l,mid),build(rs,mid+1,r);
}
void insert(int p,int l,int r,int x,Node v){
if(l==r){
c[p]=v;
return ;
}
pushdown(p);
if(mid>=x)insert(ls,l,mid,x,v);
else insert(rs,mid+1,r,x,v);
pushup(p);
}
void change(int p,int l,int r,int L,int R,int v){
if(L<=l&&r<=R)return Tag(p,v);
pushdown(p);
if(mid>=L)change(ls,l,mid,L,R,v);
if(mid<R)change(rs,mid+1,r,L,R,v);
pushup(p);
}
void change(int l,int r,int v){
if(l>r)return ;
change(1,1,n+m,l,r,v);
}
void print(int p,int l,int r){
write(l),Spa,write(r),Spa,write(c[p].mi),Spa,write(c[p].mx),Spa,write(c[p].ans),Nxt;
if(l==r)return ;
print(ls,l,mid),print(rs,mid+1,r);
}
#undef ls
#undef rs
#undef mid
}Set;
signed main(){
read(n),read(m),read(D);
for(int i=1;i<=n;i++){
read(a[i]);
tmp[i]={a[i],i};
}
for(int i=1;i<=m;i++){
read(a[n+i]);
tmp[n+i]={a[n+i],n+i};
}
sort(tmp+1,tmp+n+m+1);
for(int i=1;i<=n+m;i++)
rk[tmp[i].second]=i;
Set.build(1,1,n+m);
for(int i=1,nowrk;i<=n;i++){
nowrk=bit.query(rk[i]);
Set.insert(1,1,n+m,rk[i],Node(a[i]-1ll*D*nowrk,a[i]-1ll*D*nowrk,0));
bit.add(rk[i],1);
Set.change(rk[i],n+m,-D);
}
for(int i=n+1,nowrk;i<=n+m;i++){
nowrk=bit.query(rk[i]);
Set.insert(1,1,n+m,rk[i],Node(a[i]-1ll*D*nowrk,a[i]-1ll*D*nowrk,0));
bit.add(rk[i],1);
Set.change(rk[i],n+m,-D);
ll tmp=Set.c[1].ans;
if(tmp&1)printf("%lld.5 ",tmp>>1);
else printf("%lld ",tmp>>1);
}
}

浙公网安备 33010602011771号