【题解】LOJ#539. 「LibreOJ NOIP Round #1」旅游路线【矩阵快速幂 DP】

题目链接

题意

某城市有 \(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;
}
posted @ 2020-10-07 18:08  破壁人五号  阅读(156)  评论(0编辑  收藏  举报