CodeTON Round 2 (Div. 1 + Div. 2) - E. Count Seconds

思维 + 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--\)

这张图有且仅有一个汇点,求多少秒后每个点都没有货物

思路

  1. 如果能求出最终 tot 个物品可以流到汇点 T,那么时间一定 >= T, 因为 T 并非一直都在流出,有某些时间可能在等待流入
  2. tot 可按拓扑排序dp, 设 \(f[u]\) 为流过 u 的物品数,则在拓扑排序中执行 \(f[v]+=f[u]\) ,汇点的 \(f\) 即为 tot
  3. 考虑在什么情况下 tot == 总时间:第一个离汇点 T 最远的点的物品能流到 T 后,之后肯定是源源不断的向 T 流入
  4. 因此只需要模拟前 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;
}
posted @ 2022-09-22 18:39  hzy0227  阅读(27)  评论(0编辑  收藏  举报