思维 + DP
[Problem - E - Codeforces](https://codeforces.com/contest/1695/problem/D2)
题意
给一张有 \(n\) 个结点 \(m\) 条有向边的有向无环图,\(1<=n,m<=1000\), 每个点初始有 \(a_i\) 个物品,对于每条边 \(u->v_i\), 如果 \(u\) 上有货物,将会往每个 \(v_i\) 传递一个, u 的货物再减少一个,即 当\(a_u>0\) 时, \(a_{v_i}++\), \(a_u--\)
这张图有且仅有一个汇点,求多少秒后每个点都没有货物
思路
- 如果能求出最终 tot 个物品可以流到汇点 T,那么时间一定 >= T, 因为 T 并非一直都在流出,有某些时间可能在等待流入
- tot 可按拓扑排序dp, 设 \(f[u]\) 为流过 u 的物品数,则在拓扑排序中执行 \(f[v]+=f[u]\) ,汇点的 \(f\) 即为 tot
- 考虑在什么情况下 tot == 总时间:第一个离汇点 T 最远的点的物品能流到 T 后,之后肯定是源源不断的向 T 流入
- 因此只需要模拟前 n 秒,之后 dp 求出 tot,n + tot 即为答案
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
using namespace std;
#define endl "\n"
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e3 + 10, mod = 998244353;
int n, m;
int din[N];
ll a[N], tmp[N];
ll f[N];
vector<vector<int> > G(N);
void add(int u, int v)
{
G[u].push_back(v);
din[v]++;
}
bool check()
{
for (int i = 1; i <= n; i++)
if (a[i] > 0)
return false;
return true;
}
ll solve()
{
if (check())
return 0;
for (int t = 1; t <= n; t++)
{
for (int u = 1; u <= n; u++)
tmp[u] = a[u];
for (int u = 1; u <= n; u++)
{
if (a[u] == 0)
continue;
tmp[u]--;
for (int v : G[u])
tmp[v]++;
}
for (int u = 1; u <= n; u++)
a[u] = tmp[u];
if (check())
return t;
}
for (int i = 1; i <= n; i++)
f[i] = a[i] % mod;
queue<int> q;
for (int i = 1; i <= n; i++)
if (!din[i]) q.push(i);
int t;
while(!q.empty())
{
int u = q.front();
q.pop();
t = u;
for (int v : G[u])
{
f[v] = (f[v] + f[u]) % mod;
din[v]--;
if (!din[v])
q.push(v);
}
}
ll ans = (f[t] + n) % mod;
return ans;
}
void init()
{
fill(din, din + n + 2, 0);
for (int i = 1; i <= n; i++)
G[i].clear();
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--)
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
init();
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
add(u, v);
}
cout << solve() << endl;
}
return 0;
}
标签:Count,return,Seconds,ll,tot,int,Div,include
From: https://www.cnblogs.com/hzy717zsy/p/16720487.html