题意
某城市有 \(n\) 个景点,\(m\) 条有向边,边有长度 \(l\)。某人驾车,初始油量为 \(0\),油箱容量上限为 \(C\)。每个点有加油站,油量 \(c_i\),车在此地时,可以花费 \(p_i\) 让油箱装有 \(\max(C,c_i)\)。车每沿一条边走,会消耗 \(1\) 的油。
\(T\) 次独立的询问:从 \(s\) 出发,带着 \(q\) 元钱,经过不小于 \(d\) 的路程,最多还剩多少钱?
\(n\leq 100\),\(m\leq 10^3\),\(C,T\leq 10^5\),\(l\leq n\),\(p_i,c_i\leq 10^5\),\(q\leq n^2\),\(d\leq 10^9\)。
题解
先预处理出 \(i\) 到 \(j\),经过至多 \(2^k\) 条边,最长的距离。这是明显是一个伪·最长路,可以写成建立在加法和取 \(\min\) 运算的矩阵乘法形式。使用通过 \(2^{k}\) 条边的矩阵可以很容易求出通过 \(\min(C,c_i)\) 条边的矩阵。由于我们只关心这个矩阵的一行,我们用一个向量来乘它来减少复杂度。于是我们得到从 \(i\) 加满油出发,到达 \(j\) 的最长路。利用它来 DP:\(f[i,j]\) 代表从 \(i\) 出发带着 \(j\) 元钱的最长路。DP 完毕后每次询问二分即可。复杂度 \(O(n^2q+n^3\log c+T\log q)\)
代码:
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
#define ll long long
const int N=103;
ll mat[N][N];
ll ma[20][N][N];
int n,m,C,T;
int c[N],p[N];
ll dis[N][N];
ll f[N][N*N];
void _(ll &x,ll y){ x=(x>y?x:y); }
void mul(const ll (*x)[103],const ll (*y)[103],ll (*z)[103]){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
z[i][j]=max(z[i][j],x[i][k]+y[k][j]);
}
}
}
}
ll vec[N],wec[N];
int main(){
n=getint(),m=getint(),C=getint(),T=getint();
for(int i=1;i<=n;i++)p[i]=getint(),c[i]=min(C,getint());
memset(mat,0xd0,sizeof(mat));
for(int i=0;i<m;i++){
int x=getint(),y=getint(),z=getint();
mat[y][x]=max(mat[y][x],(ll)z);
}
for(int i=1;i<=n;i++)mat[i][i]=0;
memset(ma,0xd0,sizeof(ma));
memcpy(ma[0],mat,sizeof(ma[0]));
for(int i=1;i<20;i++){
mul(ma[i-1],ma[i-1],ma[i]);
}
for(int i=1;i<=n;i++){
memset(vec,0xd0,sizeof(vec));
vec[i]=0;
for(int j=0;j<20;j++){
if((c[i]>>j)&1){
memset(wec,0xd0,sizeof(wec));
for(int p=1;p<=n;p++){
for(int q=1;q<=n;q++){
wec[p]=max(wec[p],vec[q]+ma[j][p][q]);
}
}
memcpy(vec,wec,sizeof(vec));
}
}
memcpy(dis[i],vec,sizeof(dis[i]));
}
int qlim=n*n;
memset(f,0xd0,sizeof(f));
for(int i=1;i<=n;i++)f[i][0]=0;
for(int i=0;i<qlim;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
if(i+p[k]>qlim)continue;
_(f[k][i+p[k]],f[j][i]+dis[k][j]);
}
}
}
for(int j=1;j<=n;j++)for(int i=1;i<=qlim;i++)_(f[j][i],f[j][i-1]);
while(T --> 0){
int s=getint(),q=getint(),d=getint();
int qq=lower_bound(f[s],f[s]+q+1,d)-f[s];
printf("%d\n",q-qq);
}
return 0;
}