「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; }
作者:Ra1nbow
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。