题解 GD240526A【高速公路】
NOI2024广东集训-day5, round4, problem A
题目描述
https://matrix.sysu.edu.cn/d/GT2024/p/13
https://oi.gdcpc.cn/d/GT2024/p/13
国有 个城市。所有城市被划分为 个区域,每个城市属于一个区域,可能存在空区域。区域用英文字母编号。第一个区域编号为 ,第二个区域编号为 ,,第 个区域编号为 ,第 个区域编号为 ,第 个区域编号为 保证 ,因此编号总可以用某个小写或大写字母表示。
国国王 修建了 条双向的高速公路,保证每个城市恰好在一条高速公路上。如果某条高速公路顺次连接了城市 ,沿着高速公路从 到 需要花费 单位时间。
接着,国王 又在每个区域中修建了一个量子传送器。因此,如果城市 属于同一区域,沿着传送器从 到 只需要花费 单位时间。
设 表示如果可以任意利用高速路与传送器,从城市 到城市 最少需要的时间。请你求出 的值。输入保证从任意城市能到任意城市。
\(n\leq 10^6\)。\(K\leq 20\) 或者 \(K=30\) 且数据随机。
solution
令
- 城市 \(u\) 所在的区域是 \(bel_u\)。
- 题目所求的两个城市 \(u, v\) 之间的最短路长度为 \(dis(u, v)\)。
- 对区域 \(i\) 与城市 \(u\) 定义最短路 \(g(i, u)\),表示从区域 \(i\) 中任意一个城市出发到达城市 \(u\) 的最短路长度。
- 对区域 \(i, j\) 定义最短路 \(d(i, j)\),表示从区域 \(i\) 中任意一城到区域 \(j\) 中任意一城的最短路长度。
观察到以下性质:
- \(d(i, bel_u) \leq g(i, u)\leq d(i, bel_u) + 1\)。
- \(d(bel_u, bel_v)\leq dis(u, v)\leq d(bel_u, bel_v)+2\)。
因为 \(d(i, j)\) 与 \(g(i, u)\) 都可以在 \(O(nK)\) 的时间复杂度内计算,现在考虑 \(dis(u, v)\) 如何取值。枚举 \(bel_u=i, bel_v=j\),考虑计算有多少对 \(dis(u, v)\) 取得 \(d(i, j)\),多少对 \(dis(u, v)\) 取得 \(d(i, j) + 1\),剩余的就是 \(d(i, j) + 2\)。
假使城市 \(u, v\) 的最短路使用了区域 \(k\) 的量子传送器,则会有 \(dis(u, v)=\min_k\{g(u, k)+g(v, k)+1\}\)(表示 \(u\) 先到达区域 \(k\),然后进行一次传送,再到达 \(v\));若最短路上不使用量子传送器,则因为 \(dis(u, v)\leq 2k\) 且 \(u, v\) 会在同一条高速公路上,这部分可以暴力计算,去除错误贡献,这样我们钦定必须使用量子传送器。
此时,对城市 \(u\) 设 \(S_u\) 表示一个 \(K\) 元集合,若 \(k\in S_u\),则表示 \(g(u, k)=d(bel_u, k)\),否则 \(g(u, k)=d(bel_u, k)+1\)。
因为 \(d(i, j)=\min_k\{d(i, k)+d(k, j)+1\}\),设其中取到最小值的 \(k\) 的集合为 \(T_{i, j}\),取得最小值 \(+1\) 的 \(k\) 的集合为 \(T'_{i, j}\),同样是 \(K\) 元集合。则我们可以声称:
- \(dis(u, v)=d(i, j)\iff S_u\cap S_v\cap T_{i, j} \neq \varnothing\)。
- \(dis(u, v)=d(i, j)+2\iff S_u\cap S_v\cap T'_{i, j}=\varnothing\land (S_u\cup S_v)\cap T_{i, j}=\varnothing\)。
这样以后可以高维前缀和计算。具体计算方法:
- 枚举 \(i\),记 \(a[S]=\sum_{bel_u=i, S_u\subseteq S}1\),显然可以 fwt。
- 枚举 \(j\),枚举 \(bel_v=j\)。
- 现在需要统计 \(S_u\cap(S_v\cap T_{i, j})\neq\varnothing\) 的 \(u\),将问题取反变为求 \(=\varnothing\) 的,就是钦定了 \(S_u\) 的 \(S_v\cap T_{i, j}\) 这些位不允许有。
- 即 \(a[\complement_U({S_v\cap T_{i, j}})]\),最后用全集的答案减掉。
- 再统计 \(S_u\cap S_v\cap T'_{i, j}=\varnothing\land (S_u\cup S_v)\cap T_{i, j}=\varnothing\),重写为 \(S_u\cap (S_v\cap T'_{i, j})=\varnothing\land S_u\cap T_{i, j}=\varnothing\land S_v\cup T_{i, j}=\varnothing\)。
- 最后一个条件自己判掉,然后就形如 \(S_u\cap (S_v\cap T'_{i, j})=\varnothing\land S_u\cap T_{i, j}=\varnothing\)。
- 也就是 \(S_u\cap ((S_v\cap T'_{i, j})\cup T_{i, j})=\varnothing\)。与上面相同。
- 核心是对一边做 fwt,另一边枚举,将 \((\cap)\) 看为 \((\times)\),\((\cup)\) 看为 \((+)\),应用乘法分配律化简条件。
如此的复杂度 \(O(nK+K^22^K)\)。
对于 \(K=30\) 的情况,因为数据是随机的,根据题解,本质不同的 \((bel_u, S_u)\) 只有 \(O(K^3)\) 种,于是可以将相同的合并计算。复杂度 \(O(nK+K^6)\)。
code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
#define popcount __builtin_popcount
using LL = long long;
int tab(char ch) {
if ('a' <= ch && ch <= 'z') return ch - 'a';
return ch - 'A' + 26;
}
int n, m, K, bel[1000010], tot, d[50][50], f[50][1000010], d1[50][50];
vector<int> g[1000010], st[50];
LL ans = 0;
int S[1000010], T[50][50];
void initfd() {
memset(d, 0x3f, sizeof d);
memset(d1, 0x3f, sizeof d1);
memset(f, 0x3f, sizeof f);
for (int i = 0; i < K; i++) {
queue<int> q;
for (int p : st[i]) f[i][p] = 0, q.push(p);
vector<int> vis(K, false);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : g[u]) if (f[i][v] > f[i][u] + 1) f[i][v] = f[i][u] + 1, q.push(v);
d[i][bel[u]] = min(d[i][bel[u]], f[i][u]);
if (!vis[bel[u]]) {
vis[bel[u]] = true;
for (int v : st[bel[u]]) {
if (f[i][v] > f[i][u] + 1) f[i][v] = f[i][u] + 1, q.push(v);
}
}
}
}
for (int k = 0; k < K; k++) {
for (int i = 0; i < K; i++) {
for (int j = 0; j < K; j++) {
if (d1[i][j] > d[i][k] + 1 + d[k][j]) {
d1[i][j] = d[i][k] + 1 + d[k][j];
T[i][j] = 1 << k;
} else if (d1[i][j] == d[i][k] + 1 + d[k][j]) {
T[i][j] |= 1 << k;
}
}
}
}
}
void fandt(vector<LL>& a, int op) {
int n = a.size();
for (int k = 1, len = 2; len <= n; k <<= 1, len <<= 1) {
for (int i = 0; i < n; i += len) {
for (int j = 0; j < k; j++) {
a[i + j] += op * a[i + j + k];
}
}
}
}
void fort(vector<LL>& a, int op) {
int n = a.size();
for (int k = 1, len = 2; len <= n; k <<= 1, len <<= 1) {
for (int i = 0; i < n; i += len) {
for (int j = 0; j < k; j++) {
a[i + j + k] += op * a[i + j];
}
}
}
}
int T2[50][50];
void initST() {
for (int u = 1; u <= n; u++) {
for (int k = 0; k < K; k++) {
if (f[k][u] == d[k][bel[u]]) S[u] |= 1 << k;
}
debug("S[%d] = %d\n", u, S[u]);
for (int k = 0; k < K; k++) debug("%d%c", f[k][u], " \n"[k == K - 1]);
}
vector<LL> a[50], b[50];
for (int i = 0; i < K; i++) {
a[i].resize(1 << K);
b[i].resize(1 << K);
for (int p : st[i]) a[i][S[p]] += 1, b[i][S[p]] += 1;
fandt(a[i], 1);
fort(b[i], 1);
}
for (int i = 0; i < K; i++) {
for (int j = i; j < K; j++) {
int &T = ::T[i][j];
int &T2 = ::T2[i][j];
for (int k = 0; k < K; k++) if (d1[i][j] + 1 == d[i][k] + d[k][j] + 1) T2 |= 1 << k;
::T2[j][i] = T2;
// 80
LL cnt0 = 0, cnt2 = 0;
int U = (1 << K) - 1;
for (int v : st[j]) {
int sv = S[v];
if (sv & T) cnt0 += b[i][U] - b[i][U ^ (sv & T)];
else cnt2 += b[i][U ^ ((sv & T2) | T)];
}
// 100
// for (const auto& [su, cu] : mp[i]) {
// for (const auto& [sv, cv] : mp[j]) {
// LL c = 1ll * cu * cv;
// if (su & sv & T) cnt0 += c;
// else if ((su & sv & T2) == 0 && ((su | sv) & T) == 0) cnt2 += c;
// }
// }
LL cnt1 = (LL)st[i].size() * st[j].size() - cnt0 - cnt2;
if (i <= j) debug("i = %d, j = %d, d1[i][j] = %d, T = %d, T2 = %d, cnt0 = %lld, cnt1 = %lld, cnt2 = %lld\n", i, j, d1[i][j], T, T2, cnt0, cnt1, cnt2);
ans += (i == j ? 1 : 2) * (cnt1 + cnt2 * 2 + (LL)st[i].size() * st[j].size() * d1[i][j]);
}
}
}
int sz[1000010];
#ifdef LOCAL
int fun[110][110];
#endif
void fix() {
auto sp = [&](int u, int v) {
int i = bel[u], j = bel[v];
debug("u = %d, v = %d, i = %d, j = %d, su = %d, sv = %d, t = %d, t2 = %d, d1 = %d\n", u, v, i, j, S[u], S[v], T[i][j], T2[i][j], d1[i][j]);
return d1[i][j] + (S[u] & S[v] & T[i][j] ? 0 : (S[u] & S[v] & T2[i][j]) == 0 && ((S[u] | S[v]) & T[i][j]) == 0 ? 2 : 1);
};
#ifdef LOCAL
debug("ans = %lld\n", ans);
LL fuck = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
assert(sp(i, j) == sp(j, i));
fuck += sp(i, j);
fun[i][j] = sp(i, j);
if (i <= j) debug("dis(%d, %d) = %d\n", i, j, sp(i, j));
}
}
assert(fuck == ans);
#endif
for (int b = 1, pre = 0; b <= m; b++) {
pre += sz[b];
for (int i = pre - sz[b] + 1; i <= pre; i++) {
if (sp(i, i)) ans -= sp(i, i)
#ifdef LOCAL
, fun[i][i] = 0
, debug("dis(%d, %d) -> 0\n", i, i);
#else
;
#endif
for (int j = i + 1; j <= pre && j - i <= 2 * K; j++) {
if (sp(i, j) > j - i) ans -= 2 * sp(i, j), ans += 2 * (j - i)
#ifdef LOCAL
, fun[i][j] = fun[j][i] = j - i
, debug("dis(%d, %d) -> %d\n", i, j, j - i);
#else
;
#endif
}
}
}
#ifdef LOCAL
for (int p = 1; p <= n; p++) {
static int dis[110];
queue<int> q;
q.push(p);
memset(dis, 0x3f, sizeof dis);
dis[p] = 0;
vector<int> vis(K, false);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : g[u]) if (dis[v] > dis[u] + 1) dis[v] = dis[u] + 1, q.push(v);
if (!vis[bel[u]]) {
vis[bel[u]] = true;
for (int v : st[bel[u]]) {
if (dis[v] > dis[u] + 1) dis[v] = dis[u] + 1, q.push(v);
}
}
}
for (int q = 1; q <= n; q++) if (fun[p][q] != dis[q]) debug("! failed fun[%d][%d] = %d, but dis = %d\n", p, q, fun[p][q], dis[q]);
}
#endif
}
int main() {
#ifndef LOCAL
#ifndef NF
freopen("highway.in", "r", stdin);
freopen("highway.out", "w", stdout);
#endif
cin.tie(nullptr)->sync_with_stdio(false);
#endif
cin >> n >> m >> K;
if (K == 1) return cout << 1ll * n * (n - 1) / 2 << endl, 0;
for (int i = 1; i <= m; i++) {
string s;
cin >> s;
tot += s.size();
sz[i] = s.size();
for (int j = 0; j < (int)s.size(); j++) bel[tot - j] = tab(s[j]), st[bel[tot - j]].push_back(tot - j);
for (int j = 1; j < (int)s.size(); j++) {
if (s[j] != s[j - 1]) g[tot - j].push_back(tot - j + 1), g[tot - j + 1].push_back(tot - j);
}
}
initfd();
initST();
fix();
cout << ans / 2 << endl;
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/18213784