NOIP2020

被树上的数打爆了,滚来写没有黑题的NOIP2020。

排水系统

题意:给定一张DAG,任意点度数不超过 \(5\)

\(m\) 个点有初始容量 \(1\),一个点的容量会平均流给每条出边,求所有汇点的最终容量。

数据范围:\(1 \le n \le 10^5,\ 1 \le m \le 10\),保证任意一条源点到汇点的路径长不大于 \(11\)

估算一下最后答案的量级:

对于分母,由于每个 \(1\) 至多被除 \(11\)\(x\),上界应为 \(\text{lcm}(5^{11},4^{11}, 3^{11}, 2^{11}, 1^{11}) = 60^{11}\)

分子不会超过 \(10\) 倍分母。因此可以简单的用 i128 维护,做一遍拓扑排序即可。

submission

字符串匹配

题意:定义 \(F(A)\) 表示串 \(A\) 中出现次数为奇数的字符个数。

给定 \(S\),求 \(S = (AB)^kC\) 的拆分个数,满足 \(A, B, C\) 都不为空,且 \(F(A) \le F(C)\)

数据范围:\(\vert S\vert \le 2^{20}\)

求出串 \(S\) 的失配指针,根据经典结论,前缀 \(i\) 的最小正周期为 \(i - fail(i)\)

枚举前缀 \(i\) 作为 \(AB\),当且仅当 \(k \times i\) 的最小正周期能整除 \(i\)\(k \times i\) 能被表示为 \((AB)^i\)

预处理 \(p_i\) 表示前缀 \(i\) 的奇字符个数,同样求出 \([i, n]\) 的信息 \(s_i\)

问题转化为:有多少个 \(j \in [1, i)\) 满足 \(p_j \le s_{k \times i + 1}\)

此处用主席树复杂度就爆炸了,注意到 \(s_i \le 26\),那么统计 \(f(i, k) = \sum_{j \le i} [p_i \le k]\) 即可。

时间复杂度 \(O(26n + n \ln n)\)

submission

微信步数

题意:\(m\) 维平面,第 \(i\) 维值域为 \([1, w_i]\)

现在从某个起点开始走,\(n\) 步操作,第 \(i\) 步会使当前第 \(c_i\) 维的坐标加 \(d_i \in \{-1, 1\}\)

重复 \(n\) 步操作直到走出平面。求所有 \(\prod w\) 个起点的最终路径长度和。

数据范围:\(1 \le n \le 5 \times 10^5,\ 1 \le m \le 10\)

枚举步数,求出还没死的起点个数,即当前步对答案的贡献。

如果判断一个点死没死?由于有一维死整个都死,不妨按维考虑。

\([l_i, r_i]\) 表示第 \(i\) 维的历史最小/大位移,一个起点存活当且仅当 \(\forall 1\le i \le m,\ x_i \in[1 - l_i, w_i - r_i]\)

反过来说当前没死的节点个数等于 \(\prod (w_i - r_i + l_i)\)

容易写出 \(O(Tnm)\) 的暴力,其中 \(T\) 表示最大循环轮数(\(1 \sim n\) 被称为一轮)。

如果一轮过后回到起点,且有节点存活,则输出 \(-1\)

int inf = 1;
while(1) {
    for(auto [c, d] : op) {
        e[c] += d;
        l[c] = min(l[c], e[c]), r[c] = max(r[c], e[c]);
        if(r[c] - l[c] >= w[c]) {
            cout << ans;
            exit(0);
        }
        ll s = 1;
        for(int j = 1; j <= m; ++ j) {
            s = s * (w[j] - r[j] + l[j]) % mod;
        }
        ans = (ans + s) % mod;
    }
    if(inf) {
        for(int i = 1; i <= m; ++ i) {
            if(e[i]) {
                inf = 0;
                break;
            }
        }
        if(inf) return cout << -1, 0;
    }
}

考虑第 \(j\) 维:

在第一轮第 \(i\) 步时,历史移动最大位移为 \([l_i, r_i]\),死了 \(r_i - l_i\) 个点。

设一轮的总偏移量为 \(e_j\),则第二轮第 \(i\) 步的历史最大位移更新为 \(\left[\min(l_n,\ e_j + l_i),\ \max(r_n,\ e_j + r_i)\right]\)

\(1 \sim i\) 中新增死亡人数等于 \(\max(0, l_n - e_j - l_i) + \max(0, e_j + r_i - r_n)\),记作 \(f_{i, j}\)

容易发现以下事实:除开第一轮,第二、三……轮每步的死亡情况是一致的。

设第一轮过后还活着 \(a_j\) 个节点,接下来每轮过后会有 \(b_j\) 个起点死亡,最后一轮的 \(1 \sim i\) 步死了 \(f_{i, j}\) 个节点。

那么对于第 \(x + 2\) 轮的第 \(i\) 步,剩余存活点数等于 \(a_j - x b_j - f_{i, j}\)

所有维度的存活点数都必须大于 \(0\),显然有阈值 \(T_i = \min \lfloor\frac{a_j - f_{i, j}}{b_j}\rfloor\)\(x\)\(0\) 枚举到 \(T\)

由于最后一轮不一定走完,可能存在一个 \(k\),使得 \(T_{i < k} = T_{i \ge k} + 1\)

\[\sum_{i = 1}^n \sum_{x = 0}^{T_i}\prod_{j = 1}^m (a_j - xb_j - f_{i, j}) \]

后面是个 \(m\) 次多项式,\(O(m^2)\) 求出各项系数。

拆成 \(m + 1\) 个单项式 \(p_jx^j\),分别算 \(p_j\sum_{x = 0}^Tx^j\),这是关于 \(T\)\(j + 1\) 次多项式,插值法解决。

这样可以 \(O(j)\) 求出点值,时间复杂度 \(O(nm^2)\)

submission

移球游戏

这篇题解已经登峰造极了:https://www.luogu.com.cn/article/b69649gr

submission

posted @ 2024-10-17 20:16  Lu_xZ  阅读(34)  评论(0)    收藏  举报