[USACO 2020.1 Platinum][LOJ3248]Falling Portals(凸包+樹上倍增)


題面

https://loj.ac/problem/3248

題解

不妨設向下墜落的方向為正方向,那么世界i所處的位置為\(it-A[i](i{\geq}0)\)。那么我們可以畫出各世界的S-t圖像。

先考慮如果\(A[i]>A[Q[i]]\),即i需要追趕的情況。

如果射線i與射線j在某處相交,且j>i,那么我們稱這個點是i的“向上拐點”,是j的“向下拐點”。

那么有性質1:從i出發,遇到向上拐點就拐,那么一定是一種最優方案。(如果遇到三線共點,走該處斜率最大的一條射線)

這是因為,假設存在一種更優方案,那么它一定與我們的原方案在某點處相交,從而這與我們最優方案的構造不符。假設不成立。

  • 如圖,綠線表示假設存在的更優方案,那么在A點我們遇到了向上拐點卻沒有拐。

性質2:設up_fa[i]表示射線i出發后碰到的第一個向上拐點對應的直線(如果三線共點,那么選擇該點處斜率最大的線)

  • 如圖,up_fa[i]=j

那么,性質1中描述的方案,就是不斷從當前射線i轉入up_fa[i]而成的。

這個性質也是比較顯然的。假設性質1中的方案是圖中的黑色箭頭i->j->k,假設up_fa[j]不是k而是l,那么由於有l>j>i以及l和j均通過i下方的A點,所以l與i的交點B一定在i與j的交點之前。所以從i出發應在B點轉入l,而不是繼續走黑色路線,這與我們在性質1中規定的規則不符。

更一般地、如果按照性質1規定的方案走出的路線是\(i_1,i_2,…,i_t\),那么一旦任何一個\(i_s(2{\leq}s<t)\)使得up_fa[\(i_s\)]\({\neq}\)\(i_{s+1}\),那么\(i_{s-1}\)就一定在遇到\(i_s\)之前遇到up_fa[\(i_s\)],從而與我們在性質1中規定的規則不符。

有了性質1、2,我們就可以通過對所有i計算up_fa[i],然后通過樹上倍增求解。下面講一下計算up_fa[i]的方法。

up_fa[i],是所有\(A[j]>A[i]\)\(j>i\)的j中,使得射線\(i,j\)交點橫坐標最小的那個。射線\(i,j\)交點的橫坐標t是:

\[it-A[i]=jt-A[j] \]

\[t={\frac{A[j]-A[i]}{j-i}} \]

所以,如果我們把所有射線i,按坐標\((i,A[i])\)對應另一個平面中的一個點,所求的就是點i右上方、與i連線斜率最小的點j。(若點i右上方沒有點,說明原來的射線i與其他射線不相交,可以置up_fa[i]=0)

性質3:如果點x在點y的左上方,那么y下方的所有點的up_fa均不為x。

性質4:如果x,y,z縱坐標依次減小,且\(k(y,z)>k(x,y)>0\),(如下圖)那么z下方的所有點的up_fa均不為y。

這兩條性質顯然。由此,我們可以把所有點按縱坐標從大到小排序,然后維護一個下凸包,輪到點i的時候,按性質3、4依次從下到上刪去凸包上的點,直到不能刪,此時凸包最下方的點就是up_fa[i]。然后把i加進凸包。

至此,\(A[i]>A[Q[i]]\)的情況考慮完畢,時間復雜度是計算up_fa[]的O(n)加上樹上倍增的O(nlogn)。

對於\(A[i]<A[Q[i]]\),做法類似,只需要先計算出第一個向下拐點對應的射線down_fa[],過程中利用凸包維護,再利用樹上倍增,同樣可以求解。

總時間復雜度\(O(nlogn)\)

代碼

#include<bits/stdc++.h>

using namespace std;

#define N 200000
#define rg register
#define ll long long
#define eps 1e-8

inline ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

inline void write(ll x){
	if(x < 0)x = -x,putchar('-');
	if(x > 9)write(x / 10);
	putchar('0' + x % 10);
}

ll p[N+5],A[N+5],Q[N+5],up_fa[N+5][20],down_fa[N+5][20];

inline double k(ll j,ll i){
	return 1.0 *(A[j] - A[i]) / (j - i);
}

inline bool cmp1(ll x,ll y){
	return A[x] < A[y];
}

ll q[N+5];
ll n;

inline void calcfa(){
	ll L,R;
	q[L=R=1] = 0;
	for(rg ll i = n;i >= 1;i--){
		ll u = p[i];
		while((L<R && k(q[R],u)<0) || (L+1<R && k(q[R],u)>k(q[R-1],q[R])))R--;
		q[++R] = u;
		up_fa[u][0] = q[R-1];
	}
	for(rg ll j = 1;j <= 18;j++)
		for(rg ll i = 1;i <= n;i++)up_fa[i][j] = up_fa[up_fa[i][j-1]][j-1];
	q[L=R=1] = 0;
	for(rg ll i = 1;i <= n;i++){
		ll u = p[i];
		while((L<R && k(q[R],u)<0) || (L+1<R && k(q[R],u)>k(q[R-1],q[R])))R--;
		q[++R] = u;
		down_fa[u][0] = q[R-1];
	}
	for(rg ll j = 1;j <= 18;j++)
		for(rg ll i = 1;i <= n;i++)down_fa[i][j] = down_fa[down_fa[i][j-1]][j-1];
}

inline bool up_check(ll u,ll l){ //當前射線u遇到的第一個向下拐點是否在目標線l以上 
	if(down_fa[u][0] == 0)return 0;
	double x = k(u,down_fa[u][0]),y = (double)u * x - (double)A[u];
	return (double)l * x - y - (double)A[l] < -eps;
}

inline bool down_check(ll u,ll l){ //當前射線u遇到的第一個向上拐點是否在目標線l以下
	if(up_fa[u][0] == 0)return 0;
	double x = k(u,up_fa[u][0]),y = (double)u * x - (double)A[u];
	return (double)l * x - y - (double)A[l] > eps;
}

inline ll gcd(ll a,ll b){
	return b ? gcd(b,a % b) : a;
}

inline void print(ll u,ll v){
	ll x = A[u] - A[v],y = u - v;
	ll d = gcd(x,y);
	x /= d,y /= d;
	write(x),putchar('/'),write(y),putchar('\n');
}

int main(){
	n = read();
	for(rg ll i = 1;i <= n;i++)A[i] = read();
	for(rg ll i = 1;i <= n;i++)Q[i] = read();
	for(rg ll i = 1;i <= n;i++)p[i] = i;
	sort(p+1,p+n+1,cmp1);
	calcfa();
	for(rg ll i = 1;i <= n;i++){
		if(A[Q[i]] < A[i]){
			ll u = i; 
			if(down_check(i,Q[i])){ //需要傳送多於1次 
				for(rg ll j = 18;j >= 0;j--)if(down_check(up_fa[u][j],Q[i]))u = up_fa[u][j];
				u = up_fa[u][0];
				if(u < Q[i])puts("-1");
				else print(u,Q[i]);
			}
			else if(i < Q[i])puts("-1");
			else print(i,Q[i]);
		}
		else{
			ll u = i; 
			if(up_check(i,Q[i])){ //需要傳送多於1次 
				for(rg ll j = 18;j >= 0;j--)if(up_check(down_fa[u][j],Q[i]))u = down_fa[u][j];
				u = down_fa[u][0];	
				if(u > Q[i])puts("-1");
				else print(u,Q[i]);
			}
			else if(i > Q[i])puts("-1");
			else print(i,Q[i]);
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM