kmjp's blog

競技プログラミング参加記です

yukicoder : No.2764 Warp Drive Spacecraft

こちらの方が簡単な気はする。
https://yukicoder.me/problems/no/2764

問題

N頂点の無向グラフが与えられる。
各辺にはコストが与えられる。

また各点vにはパラメータW[v]が与えられる。
辺に沿った移動に加え、道中1回だけ任意の2点(u,v)間をコストW[u]*W[v]で特殊移動できるとする。
1番の頂点からN番の頂点に至る最小コストを求めよ。

解法

dist(u,v)を、2点u,v間を辺に沿って移動する最小コストとする。
特殊移動しない場合のコストはdist(1,N)である。
(u,v)間で移動する場合、そのコストはdist(1,u) + (W[u]*W[v]+dist(v,N))とおける。
括弧内はW[u]の1次式なので、Convex-Hull-Trickで高速に最小値を求められる。

int N,M;
ll W[202020];
vector<pair<int,ll>> E[202020];
ll dp[202020][2];


template<typename V> struct ConvexHull {
	deque<pair<V,V>> Q;
	V calc(pair<V,V> p, V x) {
		return p.first*x+p.second;
	}
	int dodo(pair<V,V> A,pair<V,V> B, pair<V,V> C) { // max or min
		return ((__int128)(A.second-C.second)*(B.first-A.first)<=(__int128)(A.second-B.second)*(C.first-A.first));
	}

	void add(V a, V b) { // add ax+b
		Q.push_back({a,b});
		int v;
		while((v=Q.size())>=3 && dodo(Q[v-3],Q[v-2],Q[v-1]))
			Q[v-2]=Q[v-1], Q.pop_back();
	}
	void add(vector<pair<V,V>> v) {
		sort(ALL(v)); 
		reverse(ALL(v)); //傾きの大きい順
		FORR2(a,b,v) add(a,b);
		
	}
	V query(V x) {
		int L=-1,R=Q.size()-1;
		while(R-L>1) {
			int M=(L+R)/2;
			(((calc(Q[M],x)>=calc(Q[M+1],x)))?L:R)=M;
		}
		return calc(Q[R],x);
	}
};
ConvexHull<ll> ch;


void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>M;
	FOR(i,N) cin>>W[i];
	FOR(i,M) {
		ll v;
		cin>>x>>y>>v;
		x--,y--;
		E[x].push_back({y,v});
		E[y].push_back({x,v});
	}
	FOR(i,N) dp[i][0]=dp[i][1]=1LL<<60;
	dp[0][0]=dp[N-1][1]=0;
	priority_queue<pair<ll,int>> Q;
	Q.push({0,0});
	Q.push({0,2*N-1});
	while(Q.size()) {
		ll co=-Q.top().first;
		int cur=Q.top().second%N;
		int id=Q.top().second/N;
		Q.pop();
		if(dp[cur][id]!=co) continue;
		FORR2(e,c,E[cur]) if(chmin(dp[e][id],co+c)) Q.push({-dp[e][id],id*N+e});
	}
	
	vector<pair<ll,ll>> V;
	FOR(i,N) V.push_back({W[i],dp[i][1]});
	ch.add(V);
	ll ret=dp[N-1][0];
	FOR(i,N) chmin(ret,dp[i][0]+ch.query(W[i]));
	cout<<ret<<endl;
	
}

まとめ

なんで歪み度の変数名Wなんだろ。Warp?