NOI_2020_D1_T1 美食家

在不考虑美食节的情况下,题目就是:从某个点出发,经过 \(T\) 时间后回到原点所能的取到的最大价值。

注意到边权 \(w_i\le 5\),那么这道题就跟 P4159 是一样的,对于每个城市拆分成五个以内的点,使得边的长度都变成 \(1\),这样就可以用矩阵进行优化,每次矩阵乘类似 Floyd 跑一个最长路。

那如果考虑美食节呢?我们又发现这个美食节个数 \(k\le 200\),也就是说可以分段跑,然后对于每一段结束之后加上这个美食节的贡献。这个时候你就可以拿到 \(55\) 分,后面 \(\operatorname{TLE}\)

分析复杂度:

\(n\le 50,T\le 10^9\)

分段运算就几乎是乘上了个 \(200\) 的大常数,而矩阵乘法跑最长路每次运算又是 \(\operatorname O((5n)^3)\),最大可以达到 \(\operatorname O(250^3)\)。每段也不止跑一次,常数可以简单算成乘一个 \(\log T\),大概是 \(\operatorname O(10^{11})\),直接爆炸。除非把时限开到100s

考虑优化:

\(\operatorname A:\) 转移矩阵实际上是可以按二进制预处理好的,这样就省去了分段造成的重复运算。(我甚至先算出最高会有几位然后再做,因为当时同学在 luogu 上被卡到十几秒,交了好几次才过,luogu 评测机浮动, 我以为这是个需要疯狂卡常的题)

\(\operatorname B:\) 最重要的一个优化,能直接把 \(n\) 的幂次减 \(1\)

我们现在有两种矩阵,一种是存贮跑了当前时间以后的答案,即从某个点出发跑了当前时间后到达一个点能拿到的最大愉悦值(不可能则为 \(-1\)),记这个矩阵为 \(ans\);一个是我们需要用到的转移矩阵的集合,记为 \(sum[]\)

是可以将 \(ans\) 这个矩阵压为一维的,只存从点 \(1\) 出发跑了若干时间到达点 \(i\) 的最大愉悦值。为什么可以这样呢?首先我们最后需要的答案是 \(ans_{1,1}\)(表示从 \(1\) 出发花了 \(T\) 时间回到 \(1\) 能拿到的最大愉悦值),跟 \(1\) 无关的处理出来也用不上。

然后考虑维护过程中这些东西是否是无用的。

如果我要维护 \(ans_{1,1}\) ,它可以由 \(ans_{1,j}+sum[]_{j,1}\) 得来

\(\operatorname {Question}:\) 那我维护这个 \(ans_{1,j}\) 不是有可能要用到 \(ans_{i,j},i!=1\) 的吗?

\(\operatorname {Answer}:\) 对于运算

\[ans=ans \times sum[x_1] \times sum[x_2] \dots \times sum[x_k] \]

你需要用到的 \(ans_{i,j},i!=1\) 是往前两步的 \(ans\) 的,是可以由状态转移矩阵来更新的,并不会缺失状态。

这么说感觉有点抽象,用式子表达就是:

\[ans_{1,1}=ans_{1,1}+sum[x_p]_{1,j}+sum[x_{p+1}]_{j,1} \]

对于 \(ans_{1,i}\) 也是一样的

\[ans_{1,i}=ans_{1,i}+sum[x_p]_{i,j}+sum[x_{p+1}]_{j,i} \]

结论:对于任意的 \(ans_{i,j},i!=1\) 都是无用的状态

我是把 \(ans\) 矩阵只考虑第一行的状态,这样前面预处理的乘法和跑答案的乘法是不一样的,你反正 \(ans\) 都压成一维了干脆另外写个结构体,然后重载两次乘法

那么到这里就已经能过了。

然后还有个没啥屁用的优化。

\(\operatorname C:\) 拆点的时候需要一个开一个,不要直接开 \(n\times 5\) 个(只要数据好,完全可以变成无用的优化,但反正这个本身也不加复杂度,我平时也都是这么写的)

当时交上去发现跑得很快,挤到了第一页,然后在其他地方优化了下常数,加了个火车头,就成功跑到了最优解。

接下来附上巨丑代码:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int MAN=250;
const int MAM=502;
struct milk
{
    LL a[MAN+2][MAN+2];
    inline void build(){memset(a,-0x3f,sizeof(a));}
};
struct stan
{
    LL a[MAN+2];
};
struct cow
{
    int t,x,y;
}b[MAM];
int n,m,t,k;
int nam;
int c[53];
int to[MAN];
milk sum[40];
milk st;
inline bool myru(cow x,cow y){return x.t<y.t;}//美 食 节 不 一 定 按 时 间 顺 序 给 ! ! !
inline stan operator *(stan x,milk y)//ans × sum[]
{
    stan z;
    memset(z.a,-0x3f,sizeof(z.a));
    for(int i=1;i<=nam;i++)
    for(int j=1;j<=nam;j++)
    if(x.a[i]>=0&&y.a[i][j]>=0)
    z.a[j]=max(z.a[j],x.a[i]+y.a[i][j]);
    return z;
}
inline milk cheng(milk s)//因为只会用到自己乘自己,干脆写函数不重载了
{
    milk z;
    z.build();
    for(int k=1;k<=nam;k++)
    for(int x=1;x<=nam;x++)
    for(int y=1;y<=nam;y++)
    if(s.a[x][k]>=0&&s.a[k][y]>=0)z.a[x][y]=max(z.a[x][y],s.a[x][k]+s.a[k][y]);
    return z;
}
LL rin()
{
    LL s=0;
    char c=getchar();
    bool bj=0;
    for(;(c>'9'||c<'0')&&c!='-';c=getchar());
    if(c=='-')c=getchar(),bj=true;
    for(;c>='0'&&c<='9';c=getchar())s=(s<<1)+(s<<3)+(c^'0');
    if(bj)return -s;
    return s;
}
int main()
{
    // freopen("delicacy.in","r",stdin);
    // freopen("delicacy.out","w",stdout);
    n=rin();m=rin();t=rin();k=rin();
    st.build();
    nam=n;
    for(int i=1;i<=n;i++)c[i]=rin();
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        x=rin();y=rin();z=rin();
        for(;z>1;z--,x=to[x])if(to[x]==0)to[x]=++nam,st.a[x][nam]=0;
        st.a[x][y]=c[y];
    }
    for(int i=1;i<=k;i++)b[i].t=rin(),b[i].x=rin(),b[i].y=rin();
    sort(b+1,b+k+1,myru);

    int max_t=t-b[n].t;
    for(int i=1;i<=k;i++)max_t=max(max_t,b[i].t-b[i-1].t);
    int max_s=0;
    for(;max_t>0;max_t>>=1)max_s++;

    milk s=st;
    sum[1]=st;
    for(int i=2;i<=max_s;i++)s=sum[i]=cheng(s);
    stan ans;
    for(int i=2;i<=nam;i++)ans.a[i]=-0x3f3f3f3f3f3f3f3f;
    ans.a[1]=c[1];//一开始出发有一次城市1的愉悦值
    for(int i=1;i<=k;i++)//跑到每个美食节的举办时间
    {
        int t_s=b[i].t-b[i-1].t;
        for(int i=1;t_s>0;i++,t_s>>=1)if(t_s&1)ans=ans*sum[i];
        if(ans.a[b[i].x]>=0)ans.a[b[i].x]+=b[i].y;
    }
    int t_s=t-b[k].t;//所有美食节都结束了,跑到给定的结束时间T
    for(int i=1;t_s>0;i++,t_s>>=1)if(t_s&1)ans=ans*sum[i];
    LL ans_s=-1;
    ans_s=max(ans_s,ans.a[1]);
    printf("%lld",ans_s);
    return 0;
}
posted @ 2020-08-19 21:11  zjjws  阅读(246)  评论(0)    收藏  举报