[NOI2023] 贸易 题解
[NOI2023] 贸易 题解
Description
给定一个 \(2^n-1\) 个节点的完美二叉树,其中每条边均为单向边,方向均为儿子连向祖先。
此外,给定 \(m\) 条前向边,即由祖先连向其后代的单向边。
树边与前向边均有对应的长度 \(w\)。记 \(dis(u,v)\) 表示 \(u,v\) 两点间在给定图中的最短路径长度,若 \(u,v\) 不连通或 \(u=v\) ,则记 \(dis(u,v) = 0\)。求 \(\sum\limits_{u=1}^{2^n-1} \sum\limits_{v=1}^{2^n-1} dis(u,v)\) 的值。
\(2 \le n \le 18, 1 \le m \le 2^n\)。
Solution
此题有 40 pts Floyd 暴力。我们来思考正解。
考虑把贡献挂到一个点上,即对于点 \(u\),计算所有点到它的最短路之和。
对于 \(u\) 子树内的点,能且只能走树边到达 \(u\),这个贡献可以一次树上 DP 算出来。
对于 \(u\) 子树外的点,一定是先走树边到达 \(u\) 父链上的某点,然后走前向边到达 \(u\),即如下图所示的结构。
假设当前枚举到 \(u\) 的祖先为 \(t\),设 \(s\) 为子树内不包含 \(u\) 的 \(t\) 的子节点,那么此时的贡献为
其中 \(F\) 表示 \(s\) 子树内所有点到 \(t\) 的最短路之和,就是走树边,刚才已经求过了。现在考虑求 \(t\) 到 \(u\) 的最短路。
考虑树上从上往下 \(dp\),在计算 \(dis(t,u)\) 时,\(dis(fa_t,u)\) 已经计算完了,那么在 \(fa_t\) 子树内跑一边 Dijkstra 即可。至于优化,可以设初始值 \(dis(t,u) = dis(fa_t,u) + w(t,fa_t)\),然后再松弛。注意松弛时需要分别考虑树内边和前向边。
注意跳祖先的时候需要判断可达性,如果某个祖先不可达,那么祖先的祖先仍然不可达。
树高为 \(n\),每个点最多 \(n\) 次最短路,时间复杂度 \(O(n^2 2^n)\)。
Code
#include <bits/stdc++.h>
#define int long long
#define inf 1e18
using namespace std;
const int N = 3e5 + 5, mod = 998244353;
int n, m, a[N], siz[N], dfn[N], curDfn, id[N], bnd[N], dep[N], f[N], g[N], dis[N][25], ans;
struct Edge {
int v, w;
friend bool operator < (Edge x, Edge y) {
return x.w > y.w;
}
};
vector<Edge> G[N];
bitset<N> vis;
void Dijkstra(int s, int depth) {
int fa = (s >> 1);
for (int i = dfn[s]; i <= bnd[s]; i++) {
vis[id[i]] = 0;
dis[id[i]][depth] = min(dis[id[i]][dep[fa]] + a[s], (int)inf);
}
priority_queue<Edge> q;
q.push({s, 0});
dis[s][depth] = 0;
while (not q.empty()) {
int u = q.top().v; q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto tmp : G[u]) {
int v = tmp.v, w = tmp.w;
if (dis[v][depth] > dis[u][depth] + w) {
dis[v][depth] = dis[u][depth] + w;
q.push({v, dis[v][depth]});
}
}
if (u != s) {
int v = (u >> 1), w = a[u];
if (dis[v][depth] > dis[u][depth] + w) {
dis[v][depth] = dis[u][depth] + w;
q.push({v, dis[v][depth]});
}
}
}
}
#define lson (u << 1)
#define rson (u << 1 | 1)
#define fa (u >> 1)
void DFS1(int u) {
siz[u] = 1;
dfn[u] = ++curDfn;
id[curDfn] = u;
dep[u] = dep[fa] + 1;
if (lson <= n) {
DFS1(lson);
siz[u] += siz[lson];
f[u] = (f[u] + f[lson] + siz[lson] * a[lson] % mod) % mod;
}
if (rson <= n) {
DFS1(rson);
siz[u] += siz[rson];
f[u] = (f[u] + f[rson] + siz[rson] * a[rson] % mod) % mod;
}
bnd[u] = curDfn; // Reverse
}
void DFS2(int u) {
int t = u;
while (t != 1) {
if (dis[u][dep[t >> 1]] == inf) break;
g[u] = (g[u] + (f[t >> 1] - f[t] + mod - siz[t] * a[t] % mod + mod) % mod + (siz[t >> 1] - siz[t]) * dis[u][dep[t >> 1]] % mod) % mod;
t >>= 1;
}
Dijkstra(u, dep[u]);
if (lson <= n) {
DFS2(lson);
}
if (rson <= n) {
DFS2(rson);
}
}
signed main() {
cin.tie(0) -> sync_with_stdio(0);
cin >> n >> m;
n = (1 << n) - 1;
for (int i = 2; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
int u, v, w; cin >> u >> v >> w;
G[u].push_back({v, w});
}
DFS1(1);
for (int i = 1; i <= n; i++) {
dis[i][0] = inf;
}
DFS2(1);
for (int i = 1; i <= n; i++) {
ans = (ans + f[i] + g[i]) % mod;
}
cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号