「LibreOJ NOIP Round #1」旅游路线

这道题真的好棒啊.. 不愧是WA爷出的题啊


题目大意

有$n$个点$m$条有向边,在每个点可以花$p_i$的钱加油至$min(C,c_i)$,每经过一条边会耗费$1$的油量

给出$T$个询问,每次询问从$s$出发,$q$块钱,走$d$的路程最多剩下多少钱


我好菜啊..

只能想到设$f_{i,q,c}$为走到$i$节点剩下$q$块钱还有$c$油量的最大路程来dp..

但是没法摆脱油量的限制

题解的做法很棒

就是设$f_{i,q}$为走到$i$节点并且在这里加油还剩下$q$块钱的最大路程,然后枚举下一次加油的地方来dp

那问题现在就转化为求从$i$到$j$不经过$min(C,c_i)$条边的最大路程

然后发现这个东西可以倍增求就很棒了


code

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
const LL Maxn = 110;
const LL Maxp = 100010;
const LL lg = 17;
const LL inf = (LL)1<<60;
LL f[Maxn][Maxn*Maxn];
LL g[lg][Maxn][Maxn];
LL w[Maxn][Maxn];
LL n, m, C, T;
LL p[Maxn], c[Maxn];
LL bit[Maxp];
LL A[Maxn], B[Maxn];
LL _max(LL x, LL y) { return x > y ? x : y; }
LL _min(LL x, LL y) { return x < y ? x : y; }
int main() {
	LL i, j, k;
	scanf("%lld%lld%lld%lld", &n, &m, &C, &T);
	for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) g[0][i][j] = i == j ? 0 : -inf;
	for(i = 1; i <= n; i++) scanf("%lld%lld", &p[i], &c[i]);
	for(i = 1; i <= m; i++){
		LL x, y, d;
		scanf("%lld%lld%lld", &x, &y, &d);
		g[0][x][y] = _max(g[0][x][y], d);
	}
	for(LL x = 1; x < lg; x++){
		for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) g[x][i][j] = -inf;
		for(k = 1; k <= n; k++){
			for(i = 1; i <= n; i++){
				for(j = 1; j <= n; j++) g[x][i][j] = _max(g[x][i][j], g[x-1][i][k]+g[x-1][k][j]);
			}
		}
	}
	for(i = 2; i <= 100000; i++) bit[i] = bit[i>>1]+1;
	for(i = 1; i <= n; i++){
		for(j = 1; j <= n; j++) A[j] = i == j ? 0 : -inf;
		LL u = _min(C, c[i]);
		for(int x = 0; x < lg; x++){
			if(u&(1<<x)){
				for(j = 1; j <= n; j++){
					B[j] = -inf;
					for(k = 1; k <= n; k++){
						B[j] = _max(B[j], A[k]+g[x][k][j]);
					}
				}
				for(j = 1; j <= n; j++) A[j] = B[j];
			}
		}
		for(j = 1; j <= n; j++) w[i][j] = A[j];
	}
	for(i = 0; i <= n*n; i++){
		for(j = 1; j <= n; j++){
			if(i < p[j]){ f[j][i] = 0; continue; }
			f[j][i] = -inf;
			for(k = 1; k <= n; k++) f[j][i] = _max(f[j][i], f[k][i-p[j]]+w[j][k]);
		}
	}
	while(T--){
		LL s, q, d;
		scanf("%lld%lld%lld", &s, &q, &d);
		if(f[s][q] < d){ printf("-1\n"); continue; }
		LL L = 0, R = q, ret;
		while(L <= R){
			LL mid = L+R>>1;
			if(f[s][mid] >= d){ ret = mid; R = mid-1; }
			else L = mid+1;
		}
		printf("%lld\n", q-ret);
	}
	return 0;
}

  

posted @ 2017-11-09 14:38  Ra1nbow  阅读(374)  评论(0编辑  收藏  举报