LOJ2392 JOISC2017 煙花棒 二分、貪心


傳送門


先二分一個最大速度\(v\)

分析移動的性質。很顯然的事情是在火焰兩邊的所有人都會往火焰的方向以最快的速度運動,這樣可以使當前位置更早獲得火焰,同時當前擁有火焰的若干個人為了傳遞火焰自然也會以最快的速度移動。

接下來考慮某個沒有火的人碰上了有火的人之后決策如何。假設有火的人\(A\)碰上了無火的人\(B\),如果\(A,B\)接下來要去的方向是一致的,那么肯定一起走直到\(A\)滅了再點燃\(B\);否則可以發現在碰上的瞬間點火和先\(AB\)一起走直到\(A\)無火時給\(B\)點火,這兩種方案\(B\)能夠點到火對應的相對距離范圍是一致的。

所以我們可以認為\(AB\)相遇則一定會在一起跑,相當於給火焰增加了\(Tsec\)的燃燒時間,也就是說在任何時刻只會有最多一個位置有火。

那么我們實際需要做的決策就是在兩個方向中選擇一個方向讓火往這邊跑,將第一個相遇的位置加入火焰,再去做決策。不難發現在上述論述下,火焰向一邊跑了之后到另一邊所有人的相對距離不變,所以對於每一個人,如果火焰下一次選擇它,那么消耗的時間是固定的,能夠獲得的時間也是固定的。我們把這兩個值稱為其權值,記為\((a_i,b_i)\)

接下來的決策過程:從火焰的左邊和右邊找到第一個位置滿足當火焰和這個位置相遇時時間相比開始增加。如果火焰可以往其中一個方向走到達這樣的位置那么就走然后繼續這個過程,否則肯定無解。

接下來到了走兩邊都一定讓時間減少的部分,這里不能直接選擇減的最少的位置走,因為這樣的走法可能會影響決策集合從而導致無解。考慮一個常見的貪心Trick:我們已知過程結束時火焰的時間,那么我們倒着考慮,相當於把火焰左右的兩個部分分別\(reverse\)\((a_i,b_i)\)交換然后做上述過程。這樣不難證明從火焰左邊的任何位置到最左端的位置一定能夠獲得正時間,右邊同理。這樣我們就可以通過這個問題是否有解得到整個問題是否有解了。

關於最后的Trick可以參考BZOJ3709

#include<bits/stdc++.h>
using namespace std;

int read(){
	int a = 0; char c = getchar(); while(!isdigit(c)) c = getchar();
	while(isdigit(c)){a = a * 10 + c - 48; c = getchar();} return a;
}

#define int long long
#define eps 1e-10
#define PII pair < int , int >
const int _ = 1e5 + 7;
int X[_] , N , K , T;

bool check(int sum , vector < PII > &A , vector < PII > &B){
	int pos1 = 0 , pos2 = 0; if(sum < 0) return 0;
	while(pos1 < A.size() || pos2 < B.size())
		if(pos1 < A.size() && sum + A[pos1].first >= 0) sum += A[pos1++].second;
		else if(pos2 < B.size() && sum + B[pos2].first >= 0) sum += B[pos2++].second;
		else return 0;
	return 1;
}

#define dis(a , b) ((X[a] - X[b]) / 2)

bool check1(int l1 , int r1 , int mid){
	vector < PII > P , Q; int l = 0 , r = N + 1;
	while(l < l1){
		int pl = l , mn = 1e18;
		while(pl != l1){
			++pl; mn = min(mn , dis(pl , l + 1) - (pl - l) * T * mid);
			if(dis(pl + 1 , l + 1) - (pl - l) * T * mid >= 0) break;
		}
		P.push_back(PII(mn , dis(pl + 1 , l + 1) - (pl - l) * T * mid)); l = pl;
	}
	while(r > r1){
		int pr = r , mn = 1e18;
		while(pr != r1){
			--pr; mn = min(mn , dis(r - 1 , pr) - (r - pr) * T * mid);
			if(dis(r - 1 , pr - 1) - (r - pr) * T * mid >= 0) break;
		}
		Q.push_back(PII(mn , dis(r - 1 , pr - 1) - (r - pr) * T * mid)); r = pr;
	}
	return check(T * mid * N - dis(N , 1) , P , Q);
}

bool check(int mid){
	int l = K , r = K; vector < PII > P , Q;
	while(l != 1){
		int pl = l , mn = 1e18;
		while(pl != 1){
			mn = min(mn , (l - pl) * T * mid - dis(l , pl - 1));
			--pl; if(dis(l , pl) <= (l - pl) * T * mid) break;
		}
		if(dis(l , pl) <= (l - pl) * T * mid) P.push_back(PII(mn , (l - pl) * T * mid - dis(l , pl)));
		else break;
		l = pl;
	}
	while(r != N){
		int pr = r , mn = 1e18;
		while(pr != N){
			mn = min(mn , (pr - r) * T * mid - dis(pr + 1 , r));
			++pr; if(dis(pr , r) <= (pr - r) * T * mid) break;
		}
		if(dis(pr , r) <= (pr - r) * T * mid) Q.push_back(PII(mn , (pr - r) * T * mid - dis(pr , r)));
		else break;
		r = pr;
	}

	return check(T * mid , P , Q) && check1(l - 1 , r + 1 , mid);
}

signed main(){
	N = read(); K = read(); T = read() * 2; for(int i = 1 ; i <= N ; ++i) X[i] = read() * 2;
	int L = 0 , R = 2e9 / T + 1;
	while(L < R){
		int mid = (L + R) >> 1;
		check(mid) ? R = mid : L = mid + 1;
	} cout << L; return 0;
}


免責聲明!

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



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