zoukankan      html  css  js  c++  java
  • 【题解】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;
    }
    
    知识共享许可协议
    若文章内无特别说明,公开文章采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
  • 相关阅读:
    看了前辈缠中说禅及其反响,忍不住想说些东西
    利弗莫尔的操盘精华篇
    缠中说禅:教你炒股票108课(转载)
    评温斯坦的炒股书(非常重要,常看看)
    本散女2
    使用PHP-GTK编写一个windows桌面应用程序
    php.exe php-cgi.exe php-win.exe的区别
    php调试利器之phpdbg
    yaf框架安装配置
    phalcon框架安装
  • 原文地址:https://www.cnblogs.com/wallbreaker5th/p/13778261.html
Copyright © 2011-2022 走看看