题解: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);
	}
}
posted @ 2026-05-23 22:20  陈牧九  阅读(9)  评论(0)    收藏  举报