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

浙公网安备 33010602011771号