拓扑排序复习
表达式树的优化
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;
}