• [NOI2020]美食家


    本题要用矩阵快速幂。

    这类问题是这样的:边有边权,求从 i 出发经过恰好 k 条边走到 j 的最长路。

    对这种题可以想到 DP。设 dpk,i,j 表示从 i 出发经过恰好 k 条边走到 j 的最长路,G 为邻接矩阵,则有转移

    dpk,i,j=maxp{dpk−1,i,p+Gp,j}

    定义一个广义矩阵乘法

     Ci,j=maxk{Ai,k+Bk,j}

     把 dpk 看成矩阵,则上式可以改写为

     dpk=dpk−1×G

     因为这个广义矩阵乘法满足结合律,所以有

     dpk=dp0×G^k

     这样子就可以做到 O(n^3log⁡k)。

    首先,我们把每个点的点权作为它所有入边的边权,再特判起始点的点权(即把 dp0,1,1 设成 c1)即可。

    其次,因为 wi 很小,所以考虑把边 e 拆成若干个点 ei 表示在这条边上的第 i 天。这样子对于原图中一条边 e=(u,v,w),我们可以拆成 (u,e1,0),(e1,e2,0),⋯,(ew−2,ew−1,0),(ew−1,v,cv)(w=1 时是 (u,v,cv))。

    最后,本题中还有 k 个美食节。我们只需要在时间相邻的两个美食节间乘 G 转移,然后在美食节那一天乘一个举办城市入边边权加上了 y 的邻接矩阵转移即可。

    这个点数是 n+4m 的 

    冷静分析一下可以发现我们并没有必要拆边,而是可以直接拆点。对于从 u 花 w 天走到 v 这个过程,我们可以看成在 u 待了 w−1 天且只在第一天获得点权,然后在第 w 天到达 v。这样子对于每个点 u 拆成若干个点 ui 表示在 u 待的第 i 天即可,连边类似。这样子点数就是 5n 的了。

    如果朴素地实现复杂度是 O((5n)^3klog⁡t) 的

    显然过不去。因为我们只需要知道 dpT,1,1,所以我们只需要保留 dpk 的第一行,再预处理 G 的 2k 次幂即可做到 O((5n)3log⁡T+(5n)2klog⁡T)。

    #include<bits/stdc++.h>
    #define ll long long 
    using namespace std;
    const int N=255;
    int n,m,T,k,c[N],id[N][6],tot;
    inline int read() {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return x*f;
    }
    struct f {int t,x,y;} t[N];
    bool operator <(f a,f b) {return a.t<b.t;}
    struct Matrix {
        ll s[N][N];
        Matrix() {memset(s,0xc0,sizeof(s));}
        ll* operator [](int i) {return s[i];}
    } q[31];
    ll ans[N];
    Matrix operator *(Matrix a,Matrix b) {
        Matrix c;
        for (int i=1;i<=tot;i++)
            for (int k=1;k<=tot;k++) {
                if (a[i][k]<0) continue;
                for (int j=1;j<=tot;++j)
                    c[i][j]=max(c[i][j],a[i][k]+b[k][j]);
            }
        return c;
    }
    inline ll Max(ll a,ll b) {return a>b?a:b;}
    inline void Mul(Matrix q) {
        ll b[N];
        for (int i=1;i<=tot;i++) b[i]=-1e18;
        for (int k=1;k<=tot;k++) {
            if (ans[k]<0) continue;
            for (int j=1;j<=tot;++j)
                b[j]=Max(b[j],ans[k]+q[k][j]);
        }
        for (int i=1;i<=tot;i++) ans[i]=b[i];
    }
    int main() {
        n=read(),m=read(),T=read(),k=read();
        tot=n;
        for (int i=1;i<=n;i++) c[i]=read();
        for (int i=1;i<=n;i++) id[i][0]=i;
        for (int i=1,u,v,w;i<=m;i++) {
            u=read(),v=read(),w=read();
            for (int j=1;j<w;j++) 
                if (id[u][j]==0) id[u][j]=++tot;
            for (int j=1;j<w;++j) q[0][id[u][j-1]][id[u][j]]=0;
            q[0][id[u][w-1]][v]=c[v];
        }
        for (int i=1;i<=k;i++) t[i]=(f){read(),read(),read()};
        sort(t+1,t+k+1); 
        t[0]=(f){0,0,0},t[k+1]=(f){T,0,0};
        for (int i=1;i<=30;i++) q[i]=q[i-1]*q[i-1];
        for (int i=2;i<=tot;i++) ans[i]=-1e18; 
        ans[1]=c[1];
        for (int i=1,x;i<=k+1;i++) {
            x=t[i].t-t[i-1].t;
            for (int j=30;j>=0;j--) 
                if (x & (1<<j)) Mul(q[j]);
            ans[t[i].x]+=t[i].y;
        }
        if (ans[1]<0) puts("-1");
        else printf("%lld
    ",ans[1]);
        return 0;
    }
  • 相关阅读:
    java解析XML【转载】
    呼叫中心功能、呼叫中心硬件设备、系统性能、运行环境介绍
    动态语言、静态语言 比较
    zk 常用资料整理(转)
    zk 的Fileupload 一直返回NULL 解决办法
    16个不错的Visual Studio插件(转)
    Mysql 去除 特定字符后面的所有字符串
    MVC4学习笔记(二) MVC设计实例,5分钟内完成你的Mvc项目
    Robotium 框架学习之Class By
    Intrumentation类:ActivityInstrumentationTestCase2学习(1)
  • 原文地址:https://www.cnblogs.com/zzrblogs/p/13534753.html
Copyright © 2020-2023  润新知