嘘~ 正在从服务器偷取页面 . . .


洛谷P6145 [USACO20FEB]Timeline G 题解

题目链接:P6145 [USACO20FEB]Timeline G

题意

Bessie 在过去的 $M$ 天内参加了 $N$ 次挤奶。但她已经忘了她每次挤奶是在哪个时候了。

对于第 $i$ 次挤奶,Bessie 记得它不早于第 $S_i$ 天进行。另外,她还有 $C$ 条记忆,每条记忆形如一个三元组 $(a,b,x)$,含义是第 $b$ 次挤奶在第 $a$ 次挤奶结束至少 $x$ 天后进行。

现在请你帮 Bessie 算出在满足所有条件的前提下,每次挤奶的最早日期。

保证 Bessie 的记忆没有错误,这意味着一定存在一种合法的方案,使得:

  • 第 $i$ 次挤奶不早于第 $S_i$ 天进行,且不晚于第 $M$ 天进行;
  • 所有的记忆都得到满足;

输入格式

第一行三个整数 $N,M,C$。保证 $1 \leq N,C \leq 10^5$,$2 \leq M \leq 10^9$。

接下来一行包含 $N$ 个整数 $S_1, S_2 , \ldots, S_n$,保证 $\forall 1 \leq i \leq n$,都满足 $1 \leq S_i \leq M$。

下面 $C$ 行每行三个整数 $a,b,x$,描述一条记忆。保证 $a \neq b$,且 $1 \leq x \leq M$。

输出格式

输出 $N$ 行,每行一个整数,第 $i$ 行的数表示第 $i$ 次挤奶的最早日期。

对于 $b - a \ge c$ ,移项可得 $a + c \le b$ 。

然后我们连一条有向边 $a \to b$ ,跑一遍最长路就能得到了。每个结点的最长路初始值就是给的 $S_i$ 。

因为题目保证有解,所以这张图一定是个 DAG ,我们直接跑一遍 $\mathtt{topo_sort}$ 就好了

注意这里有一个常见误区。为什么我们不把它变成 $b + c \ge a$ ,连 $b \to a$ ,边权为 $c$ 然后跑最短路呢?

因为题目给出了 $S$ ,而 $S_i$ 的含义为 $a \ge S_a$ 。如果我们以 $S_b$ 为 $b$ 的初值去跑,可能无法满足 $a \ge S_a$ 。

从逻辑上说,我们并不知道每个结点的真实值,我们只知道他们的下界。

那么我们做的其实就是把这个过程反过来,从 $a$ 跑到 $b$ ,也就是正解的连边。

时间复杂度 $\mathcal{O}(n)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5+15))

int n,m,c,pos=1,head[N],in[N],f[N];
struct Edge { int u,v,w,next; } e[N];
void addEdge(int u,int v,int w)
{ e[++pos] = {u,v,w,head[u]}; head[u] = pos; ++in[v]; }
queue<int> q;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m >> c;
    for(int i=1; i<=n; i++) cin >> f[i];
    for(int i=1,u,v,w; i<=c; i++)
    {
        cin >> u >> v >> w;
        addEdge(u,v,w);
    }
    for(int i=1; i<=n; i++) if(!in[i]) q.push(i);
    
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i=head[u]; i; i=e[i].next)
        {
            int v = e[i].v;
            up(f[v], f[u] + e[i].w);
            if(!(--in[v])) q.push(v);
        }
    }
    for(int i=1; i<=n; i++) cout << f[i] << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录