洛谷 P9624 [ICPC 2020 Nanjing R] Certain Scientific Railgun题解-被欧捏撒嘛折磨致死

被欧捏撒嘛调成啥了,超级难写。

思路

下文先不考虑离散化。

容易发现欧捏撒嘛走过的路是连续的,如果投影到 \(x\)\(y\) 轴上,是两个连续的区间。

而如果把机器人也都映射到坐标轴上,这两个区间应该覆盖所有点。

对于确定的区间,在这一维最小的移动距离一定是 \(f(L,R)=\min(|L|,|R|)+R-L\),则总移动距离最小为 \(f(L_x,R_x)+f(L_y,R_y)\)

这里我们规定 \(L\leq 0\leq R\),因为欧捏撒嘛从 \((0,0)\) 开始移动,所以不妨假设有个机器人在 \((0,0)\),方便离散化后限制区间端点。

枚举其中一个区间(下文都规定枚举 \(x\) 轴的区间),令另一个区间极小,就可以获得 \(O(n^3)\) 的做法,而规定枚举顺序使用双指针容易优化到 \(O(n^2)\)

从大到小枚举 \(R\)(好处在后文),已知上一轮的所有 \(L\) 对应的答案,考虑 \(R\) 左移答案会有什么变化。

先考虑 \(y\) 轴。左移 \(R\) 会释放出一个元素(如果多个元素在同一与 \(y\) 轴平行的直线上都要释放,这里拿一个元素举例子),假设它的 \(y\) 坐标是 \(p_y\),如果 \(p_y\notin [L_y,R_y]\),那么区间会扩张。具体的,应该是进行 \(L_y\leftarrow \min(L_y,p_y)\)\(R_y\leftarrow \max(R_y,p_y)\),考虑使用吉司机线段树维护。

但由于两个操作是独立的,不好维护,所以转换式子:

\[\begin{aligned} f(L,R) &= \min(|L|, |R|) + R - L \\ &= \min(|L|, |R|) + |R| + |L|\\ &= \min(2|L|+|R|, |L|+2|R|)\\ &= \min(A', B') \end{aligned} \]

这样维护两项,修改的时候都是吉司机线段树经典操作,再维护 \(4\) 个区间答案,分别依赖于 \(A'\) 最(次)小、 \(B'\) 最(次)小,修改的时候把依赖于它的答案加上增量即可,这里注意区间答案的合并比较特殊。

再考虑 \(x\) 轴,\([L_x,R_x]\) 固定,把 \(f(L_y,R_y)\) 视作关于 \(L_x\) 的函数,写作 \(F_{R_{x}}(L_x)\)

\[\text{ans}=\min_{L\leq \min(0,R)}\big(\min(|L|,|R|)+R-L+F_{R_{x}}(L_x)\big) \]

对第一项分类讨论容易得出答案应该分两段,查询两次一次查 \(-L\) 一次查 \(-2L\),可以把上面说的四个区间答案维护两次,总时间复杂度 \(O(n\log^2 n)\)

没明白怎么维护的看代码即可,细节很多,不一一文字叙述。

AC code

#include <bits/stdc++.h>
#define int int64_t
//#define int __int128
//#define MOD (1000000007)
//#define eps (1e-6)
#define endl '\n'
#define debug_endl cout<<endl;
#define debug cout<<"debug"<<endl;
using namespace std;
const int MAXN=1e5+10;
const int INF=4e18;
int ax[MAXN],ay[MAXN];
int vx[MAXN],lx;
struct Segment_Beats{
#define lc (p<<1)
#define rc (p<<1|1)
	struct Node{
		int ans1[2][2],ans2[2][2];//-L 0:依赖最小,1:依赖非最小
		int lans1[2][2],lans2[2][2];//-2L
		int a_min[2],a_add;//|L|的最小,次小
		int b_min[2],b_add;//|R|的最小,次小
	};
	Node tr[MAXN<<2];
	inline void update_node(int p,int ka,int kb){
		if (ka != 0) {
			tr[p].a_min[0] += ka;
			tr[p].a_add += ka;
			for (int j = 0; j <= 1; ++j) {
				if (tr[p].ans1[0][j] != INF) tr[p].ans1[0][j] += 2 * ka;
				if (tr[p].ans2[0][j] != INF) tr[p].ans2[0][j] += ka;
				if (tr[p].lans1[0][j] != INF) tr[p].lans1[0][j] += 2 * ka;
				if (tr[p].lans2[0][j] != INF) tr[p].lans2[0][j] += ka;
			}
		}
		if (kb != 0) {
			tr[p].b_min[0] += kb;
			tr[p].b_add += kb;
			for (int i = 0; i <= 1; ++i) {
				if (tr[p].ans1[i][0] != INF) tr[p].ans1[i][0] += kb;
				if (tr[p].ans2[i][0] != INF) tr[p].ans2[i][0] += 2 * kb;
				if (tr[p].lans1[i][0] != INF) tr[p].lans1[i][0] += kb;
				if (tr[p].lans2[i][0] != INF) tr[p].lans2[i][0] += 2 * kb;
			}
		}
	}
	inline void push_down(int p,int l,int r){
		if(l<r&&(tr[p].a_add||tr[p].b_add)){
			int mina=min(tr[lc].a_min[0],tr[rc].a_min[0]),minb=min(tr[lc].b_min[0],tr[rc].b_min[0]);
			if(tr[lc].a_min[0]==mina&&tr[lc].b_min[0]==minb){
				update_node(lc,tr[p].a_add,tr[p].b_add);
			}else if(tr[lc].a_min[0]==mina){
				update_node(lc,tr[p].a_add,0);
			}else if(tr[lc].b_min[0]==minb){
				update_node(lc,0,tr[p].b_add);
			}
			if(tr[rc].a_min[0]==mina&&tr[rc].b_min[0]==minb){
				update_node(rc,tr[p].a_add,tr[p].b_add);
			}else if(tr[rc].a_min[0]==mina){
				update_node(rc,tr[p].a_add,0);
			}else if(tr[rc].b_min[0]==minb){
				update_node(rc,0,tr[p].b_add);
			}
			tr[p].a_add=tr[p].b_add=0;
		}
	}
	inline void push_up(int p){
		if(tr[lc].a_min[0]==tr[rc].a_min[0]){
			tr[p].a_min[0]=tr[lc].a_min[0];
			tr[p].a_min[1]=min(tr[lc].a_min[1],tr[rc].a_min[1]);
		}else if(tr[lc].a_min[0]<tr[rc].a_min[0]){
			tr[p].a_min[0]=tr[lc].a_min[0];
			tr[p].a_min[1]=min(tr[lc].a_min[1],tr[rc].a_min[0]);
		}else{
			tr[p].a_min[0]=tr[rc].a_min[0];
			tr[p].a_min[1]=min(tr[lc].a_min[0],tr[rc].a_min[1]);
		}
		if(tr[lc].b_min[0]==tr[rc].b_min[0]){
			tr[p].b_min[0]=tr[lc].b_min[0];
			tr[p].b_min[1]=min(tr[lc].b_min[1],tr[rc].b_min[1]);
		}else if(tr[lc].b_min[0]<tr[rc].b_min[0]){
			tr[p].b_min[0]=tr[lc].b_min[0];
			tr[p].b_min[1]=min(tr[lc].b_min[1],tr[rc].b_min[0]);
		}else{
			tr[p].b_min[0]=tr[rc].b_min[0];
			tr[p].b_min[1]=min(tr[lc].b_min[0],tr[rc].b_min[1]);
		}
		for(int c1=0;c1<=1;++c1) for(int c2=0;c2<=1;++c2) tr[p].ans1[c1][c2]=tr[p].ans2[c1][c2]=tr[p].lans1[c1][c2]=tr[p].lans2[c1][c2]=INF;
		for(int c1=0;c1<=1;++c1){
			for(int c2=0;c2<=1;++c2){
				int cu1=(tr[lc].a_min[0]==tr[p].a_min[0]&&c1==0)?0:1;
				int cu2=(tr[lc].b_min[0]==tr[p].b_min[0]&&c2==0)?0:1;
				tr[p].ans1[cu1][cu2]=min(tr[p].ans1[cu1][cu2],tr[lc].ans1[c1][c2]);
				tr[p].ans2[cu1][cu2]=min(tr[p].ans2[cu1][cu2],tr[lc].ans2[c1][c2]);
				tr[p].lans1[cu1][cu2]=min(tr[p].lans1[cu1][cu2],tr[lc].lans1[c1][c2]);
				tr[p].lans2[cu1][cu2]=min(tr[p].lans2[cu1][cu2],tr[lc].lans2[c1][c2]);
				cu1=(tr[rc].a_min[0]==tr[p].a_min[0]&&c1==0)?0:1;
				cu2=(tr[rc].b_min[0]==tr[p].b_min[0]&&c2==0)?0:1;
				tr[p].ans1[cu1][cu2]=min(tr[p].ans1[cu1][cu2],tr[rc].ans1[c1][c2]);
				tr[p].ans2[cu1][cu2]=min(tr[p].ans2[cu1][cu2],tr[rc].ans2[c1][c2]);
				tr[p].lans1[cu1][cu2]=min(tr[p].lans1[cu1][cu2],tr[rc].lans1[c1][c2]);
				tr[p].lans2[cu1][cu2]=min(tr[p].lans2[cu1][cu2],tr[rc].lans2[c1][c2]);
			}
		}
	}
	void build(int p,int l,int r,int *A,int *B){
		tr[p].a_add=tr[p].b_add=0;
		if(l==r){
			tr[p].ans1[0][0]=2*A[l]+B[l]-vx[l];
			tr[p].ans2[0][0]=A[l]+2*B[l]-vx[l];
			tr[p].lans1[0][0]=2*A[l]+B[l]-vx[l]*2;
			tr[p].lans2[0][0]=A[l]+2*B[l]-vx[l]*2;
			tr[p].a_min[1]=tr[p].b_min[1]=INF;
			tr[p].ans1[0][1]=tr[p].ans1[1][0]=tr[p].ans1[1][1]=tr[p].ans2[0][1]=tr[p].ans2[1][0]=tr[p].ans2[1][1]=INF;
			tr[p].lans1[0][1]=tr[p].lans1[1][0]=tr[p].lans1[1][1]=tr[p].lans2[0][1]=tr[p].lans2[1][0]=tr[p].lans2[1][1]=INF;
			tr[p].a_min[0]=A[l];
			tr[p].b_min[0]=B[l];
			tr[p].a_add=tr[p].b_add=0;
			return ;
		}
		int mid=(l+r)>>1;
		build(lc,l,mid,A,B),build(rc,mid+1,r,A,B);
		push_up(p);
	}
	void modify_A(int p,int l,int r,int L,int R,int k){
		if(tr[p].a_min[0]>=k) return ;
		if(l>=L&&r<=R&&tr[p].a_min[1]>k){
			update_node(p,k-tr[p].a_min[0],0);
			return ;
		}
		push_down(p,l,r);
		int mid=(l+r)>>1;
		if(L<=mid) modify_A(lc,l,mid,L,R,k);
		if(R>mid) modify_A(rc,mid+1,r,L,R,k);
		push_up(p);
	}
	void modify_B(int p,int l,int r,int L,int R,int k){
		if(tr[p].b_min[0]>=k) return ;
		if(l>=L&&r<=R&&tr[p].b_min[1]>k){
			update_node(p,0,k-tr[p].b_min[0]);
			return ;
		}
		push_down(p,l,r);
		int mid=(l+r)>>1;
		if(L<=mid) modify_B(lc,l,mid,L,R,k);
		if(R>mid) modify_B(rc,mid+1,r,L,R,k);
		push_up(p);
	}
	int query1(int p,int l,int r,int L,int R){
		if(L>R) return INF;//long long要防爆哦
		if(l>=L&&r<=R)return min(min(min(tr[p].ans1[0][0],tr[p].ans2[0][0]),min(tr[p].ans1[0][1],tr[p].ans2[0][1])),
									min(min(tr[p].ans1[1][0],tr[p].ans2[1][0]),min(tr[p].ans1[1][1],tr[p].ans2[1][1])));
		int mid=(l+r)>>1;
		int res=INF;
		push_down(p,l,r);
		if(L<=mid) res=min(res,query1(lc,l,mid,L,R));
		if(R>mid) res=min(res,query1(rc,mid+1,r,L,R));
		return res;
	}
	int query2(int p,int l,int r,int L,int R){
		if(L>R) return INF;
		if(l>=L&&r<=R)return min(min(min(tr[p].lans1[0][0],tr[p].lans2[0][0]),min(tr[p].lans1[0][1],tr[p].lans2[0][1])),
			min(min(tr[p].lans1[1][0],tr[p].lans2[1][0]),min(tr[p].lans1[1][1],tr[p].lans2[1][1])));
		int mid=(l+r)>>1;
		int res=INF;
		push_down(p,l,r);
		if(L<=mid) res=min(res,query2(lc,l,mid,L,R));
		if(R>mid) res=min(res,query2(rc,mid+1,r,L,R));
		return res;
	}
#undef lc
#undef rc
};
int T,n;
int mapy[MAXN];
int ans,yl[MAXN],yr[MAXN],zeroL,zeroR;
pair<int,int> vy[MAXN];
Segment_Beats tr;
bitset<MAXN> b;
vector<int> tx[MAXN];
void init(){
	b.reset();
	for(int i=1;i<=n+1;++i) tx[i].clear();
}
inline int f(int L,int R,pair<int,int> *a){
	if(L>R) return 0;
	return min(abs(a[R].first),abs(a[L].first))+a[R].first-a[L].first;
}
inline int f(int L,int R,int *a){
	if(L>R) return 0;
	return min(abs(a[R]),abs(a[L]))+a[R]-a[L];
}
signed main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--){
		cin>>n;
		init();
		bool flag=true;
		for(int i=1;i<=n;++i){
			cin>>ax[i]>>ay[i];
			if(ax[i]==0&&ay[i]==0) flag=false;
		}
		if(flag) ++n,ax[n]=0,ay[n]=0;//假想敌(0,0)
		for(int i=1;i<=n;++i){
			vx[i]=ax[i];
			vy[i]={ay[i],i};
		}
		sort(vx+1,vx+n+1);
		sort(vy+1,vy+n+1);
		lx=unique(vx+1,vx+n+1)-vx-1;
		for(int i=1;i<=n;++i){
			int idx=lower_bound(vx+1,vx+lx+1,ax[i])-vx;
			tx[idx].emplace_back(i);//离散化后挂载同一x坐标的
			idx=lower_bound(vy+1,vy+n+1,make_pair(ay[i],i))-vy;
			mapy[i]=idx;
		}
		zeroL=zeroR=lower_bound(vx+1,vx+lx+1,0)-vx;
		ans=f(1,n,vy);//ay满
		int l=1,r=n;
		for(int L=lx;L>=1;--L){
			for(int idx:tx[L]) b[mapy[idx]]=true;
			while(l<r&&b[l]&&vy[l+1].first<=0)++l;
			while(l<r&&b[r]&&vy[r-1].first>=0)--r;
			if(L<=zeroL){
				yl[L]=-vy[l].first,yr[L]=vy[r].first;
				ans=min(ans,f(l,r,vy)+f(L,lx,vx));
			}
		}
		tr.build(1,1,zeroL,yl,yr);
		for(int R=lx-1;R>=zeroR;--R){
			for(int idx:tx[R+1]){
				if(ay[idx]<0) tr.modify_A(1,1,zeroL,1,zeroL,-ay[idx]);
				else if(ay[idx]>0) tr.modify_B(1,1,zeroL,1,zeroL,ay[idx]);
			}
			int mid=lower_bound(vx+1,vx+lx+1,-vx[R])-vx;
			ans=min(ans,min(tr.query1(1,1,zeroL,1,mid-1)+vx[R]*2,tr.query2(1,1,zeroL,mid,zeroL)+vx[R]));
		}
		cout<<ans<<endl;
	}
	return 0;
}
posted @ 2026-08-19 10:07  司马只因锥  阅读(5)  评论(0)    收藏  举报