拓扑排序复习

表达式树的优化

AOV网

Activity on Vertex Network

基于定点的行动网络。求解顺序就是拓扑排序。

拓扑排序有多种。

拓扑排序分层,可以分成很多同一地位的层,有一定的组合意义。计算拓扑序方案数。

队列算法。

反过来,还有拓扑逆序。

用栈也可以。

AOE网

基于边,带权。

用途:(1)完成整项工程至少需要多长时间?(2)哪些活动是影响工程进度的关键?

完成的最短时间是最长路径长度,关键路径。

关键活动:对于活动i,最早开始时间e(i)=最晚开始时间l(i),那么i就是关键活动。

计算关键路径,其实是最长路的算法(DAG上dp):

证明:

车站分级

对于一辆列车,它将在 x1,x2,...,xk这些站台停靠。这说明,x1,...,xn是非严格单调递增的,进一步说明[x1,xn]中不停靠的车站级别一定小于x1,...,xn。

所以从不停靠的车站连向停靠的车站,用邻接矩阵去重,,。注意不是只连前面的,而是要向x1,...,xn都连边。这里调了半天。

最后,问题化为拓扑层数。

Directing Edges

函数调用

大致的思路:将乘法合并到加法。建图拓扑排序。

性质:加法只会受到之后的乘法的影响。按顺序连边的话,函数调用顺序会形成一个DAG,叶子一定是加法和乘法函数。

Func结构体:op,p,v; func[N];

建立一个0号点,连向a1到an,都归为加法操作。

然后输入函数信息,对于调用型的函数,连边。

最后输入调用顺序,按顺序连接从0号点出发的边。

下面设计三个函数:dfs, getw, topo

(1)dfs(int u)

计算mul[]。mul[u]表示第u号点下方的总乘积。这用记忆话搜索实现。

(2)getw()

计算边权。用边权记录u点右侧乘积的贡献。由于顺序加边,所以合理。

(3)topo()

最终统计贡献,用p[u]记录操作后每个点被扩大多少倍。

参考:泥土笨笨B站。Luogu题解。cjh的ppt。

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define CHECK cout<<"WALKED"<<endl;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
ll binpow(ll a, ll b, ll mod){ll res = 1;  while (b > 0){if (b & 1) res = res * a % mod;a = a * a % mod;  b >>= 1;  }  return res;}
using namespace std;

const int N = 2e5 + 5;
int n, m, q;
vector<pair<int, ll> > e[N];
ll mul[N], p[N]; 
struct Func{
	int op, p, v;
} func[N];
ll ans[N];
int in_deg[N];
const ll mod = 998244353;

ll dfs(int u)
{
	if (mul[u] != -1 || e[u].empty()) {
		return mul[u];
	}
	mul[u] = 1;
	for (pair<int, int> ed : e[u]) {
		mul[u] = mul[u] * dfs(ed.first) % mod;
	}
	return mul[u];
}

void getw()
{
	for (int i = 0; i <= n + m; i++) {
		ll r = 1;
		for (int j = (int)e[i].size() - 1; j >= 0; j--) {
			e[i][j].second = r;
			r = r * mul[e[i][j].first] % mod;
		}
	}
}

void topo()
{
	queue<int>q;
	p[0] = 1;
	for (int i = 0; i <= n + m; i++)
		if (!in_deg[i])
			q.push(i);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		for (int i = 0, v, w; i < (int)e[u].size(); i++) {
			v = e[u][i].first;
			w = e[u][i].second;
			p[v] = (p[v] + w * p[u] % mod) % mod;
			if (!(--in_deg[v]))
				q.push(v);
		}
	}
	
}
int main()
{
	memset(mul, -1, sizeof(mul));
	scanf("%d", &n);
	for (int i = 1, t; i <= n; i++) {
		scanf("%d", &t);
		func[i].op = 1;
		func[i].v = t;
		func[i].p = i;
		mul[i] = 1;
		e[0].push_back(make_pair(i, 0));
		in_deg[i]++;
	}
	scanf("%d", &m);
	for (int i = n + 1, c, g; i <= n + m; i++) {
		scanf("%d", &func[i].op);
		if (func[i].op == 1) {
			scanf("%d%d", &func[i].p, &func[i].v);
			mul[i] = 1;
		} else if (func[i].op == 2) {
			scanf("%d", &func[i].v);
			mul[i] = func[i].v;
		} else {
			scanf("%d", &c);
			for (int j = 1; j <= c; j++) {
				scanf("%d", &g);
				e[i].push_back(make_pair(g + n, 0));
				in_deg[g + n]++;
			}
		}
	}
	scanf("%d", &q);
	for (int i = 1, f; i <= q; i++) {
		scanf("%d", &f);
		e[0].push_back(make_pair(f + n, 0));
		in_deg[f + n]++;
	}
	dfs(0); // no return dfs
	getw();
	topo();
	for (int i = 1; i <= n + m; i++) {
		if (func[i].op == 1) {
			ans[func[i].p] = (ans[func[i].p] + func[i].v * p[i] % mod) % mod;
		}
	}
	for (int i = 1; i <= n; i++) {
		printf("%lld ", ans[i]);
	}
	return 0;
}
posted @ 2024-04-13 09:38  Vegdie  阅读(3)  评论(0编辑  收藏  举报