「NOI2020」美食家 题解

最大值矩阵快速幂+倍增

Statement

给定一张 n50 个点 m501 条边的有向图,边权 w5,点有点权 ci52501

现在想从 1 出发,经过恰好 T 时间(中途不停留),经过一条边消耗边权 w 时间,回到 1

无法到 1 输出 1 ,否则输出经过点权和的最大值

给出 k200 个附加条件,即点 x 在时刻 t 权值增加 y109

Luogu

Solution

容易想到设 f[u][j] 表示在 j 时刻走到点 u 的最大值

f[u][j]=max{f[v][jw(v,u)]}+cu+ex[u][j]

其中 ex[u][j] 表示额外权值

看到状态里面有 j ,那肯定是要上矩阵快速幂的,考虑把 cu 这个点权放到边上,1 号点特殊处理即可

mp[v][u]=cu ,那么

f[u][j]=max{f[v][jw]+mp[v][u]}+ex[u][j]

发现 w 很小,大喜过望,经由 排队 - 题目 - FZUOJ (fzoi.top) 的启发,知道了可以把 u 直接拆成 5 个点即可(一般的,这种拆点矩阵快速幂的方式,只需要关注当前最大的位置的转移即可,其他位置可以理解为仅仅是记录信息,比如在本题中,最开始只需要填写 f[x][5] 的转移即可,其他的保留信息即可)

那么这长成一个 max 的样子咋办,借用动态 DP(eg.P5024 [NOIP2018 提高组] 保卫王国) 的思想,我们知道可以重定义矩阵运算

但是还是不知道外面那个 ex 咋办,GG,我们看题解去!以下学习自:M_sea

发现其实也比较好处理,由于每两个特殊贡献的时间不一样,所以可以先将这些贡献按时间排序。

考虑在相邻两个美食节之间,做一遍普通的DP转移。例如,第 j 个时间 tj,第 j1 个时间 tj1,则这段的转移:

dp[tj]=dp[tj1]Gtjtj1

然后后令 dp[tj][xj]+=yj,即可。

也就是说,本来,我们是直接乘上一个 GT ,但是为了在转移过程中加上特殊贡献,所以我们分成 k=200 段转移。

这样暴力做 200 段转移肯定不能过,所以我们借鉴 [NOI Online #3 提高组] 魔法值 的思想,预处理 G,G2,G4,G8, ,那么对于 Gx ,就可以倍增地 log 算值了

所以总复杂度 O((wn)3logT+(wn)2klogT),小卡常,需要使用 FastIO

Code

矩阵具体大概长成这样:

#include<bits/stdc++.h>
#define int long long
#define id(x,y) (((x)-1)*5+(y))
using namespace std;
const int inf = 1e18;
const int N = 55;

char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
    int s=0,w=1; char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
    return s*w;
}
bool cmax(int&a,int b){return a<b?a=b,1:0;}
bool cmin(int&a,int b){return a>b?a=b,1:0;}

int c[N],f[N][5];
bool mp[N][N][5];
int n,m,t,K;

struct Line{int u,v,w;}line[N*10];
struct Node{
    int t,x,y;
    bool operator<(const Node&rhs)const{
        return t<rhs.t;
    }
}a[N<<2];
struct Matrix{
    int n,m;
    int mat[N*5][N*5];
    Matrix(int _n=5*N-1,int _m=5*N-1):n(_n),m(_m){
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                mat[i][j]=-1e9;//不能直接赋成 -inf(-1e18),运算中会爆 ll
    }
    Matrix operator*(const Matrix&rhs)const{
        Matrix res(n,rhs.m);
        for(int i=1;i<=n;++i)for(int j=1;j<=rhs.m;++j)for(int k=1;k<=m;++k)
            res.mat[i][j]=max(res.mat[i][j],mat[i][k]+rhs.mat[k][j]);
        return res;
    }
}A,B,T[33];

void ksm(int x){
    for(int i=0;(1<<i)<=x;++i)
        if(x>>i&1)A=A*T[i];
}

signed main(){
    n=read(),m=read(),t=read(),K=read();
    for(int i=1;i<=n;++i)c[i]=read();
    for(int i=1,u,v,w;i<=m;++i)
        u=read(),v=read(),w=read(),
        mp[v][u][w]=1,line[i]=(Line){u,v,w};
    for(int i=1;i<=K;++i)
        a[i].t=read(),a[i].x=read(),a[i].y=read();
    sort(a,a+K+1);

    for(int i=1;i<=n;++i)
        for(int j=0;j<=4;++j)
            f[i][j]=-1e9;
    f[1][0]=c[1];
    for(int j=1,k=1;j<=4;++j){
        if(j==a[k].t)c[a[k].x]+=a[k].y;
        for(int i=1;i<=m;++i)if(j>=line[i].w)
            cmax(f[line[i].v][j],f[line[i].u][j-line[i].w]+c[line[i].v]);
        if(j==a[k].t)c[a[k].x]-=a[k].y,k++;
    }

    // for(int i=1;i<=n;++i,puts(""))
    //     for(int j=0;j<=4;++j)
    //         cout<<max(f[i][j],-1ll)<<" ";

    A.n=1,A.m=n*5;
    for(int i=0;i<32;++i)T[0].n=T[0].m=5*n;
    for(int i=1;i<=n;++i)for(int j=0;j<=4;++j)A.mat[1][id(i,j+1)]=f[i][j];
    for(int i=1;i<=n;++i)for(int j=1;j<=4;++j)T[0].mat[id(i,j+1)][id(i,j)]=0;
    for(int i=1;i<=n;++i)for(int j=1;j<=5;++j)for(int k=1;k<=n;++k)
        if(i!=k&&mp[i][k][j])T[0].mat[id(k,5-j+1)][id(i,5)]=c[i];
    for(int i=1;(1<<i)<=t;++i)T[i]=T[i-1]*T[i-1];

    // for(int i=1;i<=n*5;++i,puts(""))
    //     for(int j=1;j<=n*5;++j)
    //         printf("%2lld ",max(T[0].mat[i][j],-1ll));

    int k=1,tim=4;
    while(k<=K&&a[k].t<=tim)++k;
    while(k<=K){
        if(tim<a[k].t-1)
            ksm(a[k].t-tim-1),tim=a[k].t-1;
        B=T[0];
        for(int i=1;i<=n*5;++i)
            B.mat[i][id(a[k].x,5)]+=a[k].y;
        A=A*B,++k,++tim;
    }
    if(tim<=t+4)ksm(t+4-tim);

    // for(int i=1;i<=n*5;++i,puts(""))
    //     for(int j=1;j<=n*5;++j)
    //         printf("%2lld ",max(A.mat[i][j],-1ll));

    printf("%lld\n",max(A.mat[1][1],-1ll));
    return 0;
}
posted @   _Famiglistimo  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示