#43463. 「计蒜客 2020.2 提高组」保卫水库
Summarize
给定有 $n$ 个节点和 $m$ 条边的有向无环图。每个节点有一定的水量,每个结点的水在水库被破坏后会均等地沿着出边流向其他节点。给出 $k$ 个操作,要求查询某个节点流入的水量,以及修改某个节点的初始水量。
$n\le 5000,m\le 10000,k\le 500000$
测试点的查询和修改次数比例接近 $1:1$
Solution
观察到图为 DAG,并且 $n,n-1..2,1$ 为合法拓扑序。因此可以在图中 $O(n+m)$ 动态规划求出每个城市流入的水量。
由于在某个城市中增加水量之后,流入下游各个城市的比例是固定的。因此可以预处理这些比值。
用一个容器存放所有修改改操作,对于查询,计算容器中所有修改操作对其的影响;当容器大小达到某个值 $s$ 时,执行 $O(n+m)$ 的动态规划下放更新。
复杂度为 $O(sk+\frac{k}{s}(n+m))$,当 $s$ 取 $\sqrt{n+m}$ 时,复杂度取最小值。
Note
均摊复杂度分析 「雅礼集训 2018 Day10」贪玩蓝月 「USACO 2019.12 Platinum」Bessie’s Snow Cow
循环时尽量采用 $n\times(n+m)$ 而不是 $(n+m)\times n$
Code
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 
 | #include <bits/stdc++.h>using namespace std;
 typedef long long ll;
 
 const ll MOD = 998244353;
 const int MAXN = 5005;
 
 ll qpow(ll n, ll k) {
 ll ret = 1;
 while (k) {
 if (k & 1) ret = ret * n % MOD;
 n = n * n % MOD;
 k >>= 1;
 }
 return ret;
 }
 
 int n, m, k;
 int pre[MAXN][MAXN];
 int c[MAXN];
 ll a[MAXN], b[MAXN], d[MAXN], a1[MAXN];
 ll f[MAXN][MAXN];
 
 pair<int, ll> s[MAXN];
 int mx = 0, cnt = 0;
 
 void upd() {
 for (int u = n; u >= 1; --u) {
 b[u] = 0;
 for (int i = 1; i <= c[u]; ++i) {
 int v = pre[u][i];
 b[u] = (b[u] + (a[v] + b[v]) % MOD * d[v] % MOD) % MOD;
 }
 }
 }
 
 int main() {
 scanf("%d%d%d", &n, &m, &k);
 mx = sqrt(n + m);
 for (int i = 1; i <= n; ++i)
 scanf("%lld", &a1[i]);
 for (int i = 1; i <= m; ++i) {
 int u, v;
 scanf("%d%d", &u, &v);
 if (u > v) {
 pre[v][++c[v]] = u;
 d[u]++;
 } else {
 pre[u][++c[u]] = v;
 d[v]++;
 }
 }
 for (int i = 1; i <= n; ++i)
 d[i] = qpow(d[i], MOD - 2);
 for (int u = 1; u <= n; ++u) {
 a[u] = 1;
 upd();
 for (int i = 1; i <= n; ++i)
 f[u][i] = b[i];
 a[u] = 0;
 }
 memcpy(a, a1, sizeof(a));
 upd();
 while (k--) {
 int op;
 scanf("%d", &op);
 if (op == 1) {
 int x, y;
 scanf("%d%d", &x, &y);
 s[++cnt] = make_pair(x, y);
 if (cnt == mx) {
 for (int i = 1; i <= cnt; ++i)
 a[s[i].first] += s[i].second;
 cnt = 0;
 upd();
 }
 } else {
 int x;
 scanf("%d", &x);
 ll ans = 0;
 for (int i = 1; i <= cnt; ++i)
 ans = (ans + f[s[i].first][x] * s[i].second % MOD) % MOD;
 printf("%lld\n", (ans + b[x]) % MOD);
 }
 }
 return 0;
 }
 
 |