题解 GD240526A【高速公路】

NOI2024广东集训-day5, round4, problem A

题目描述

https://matrix.sysu.edu.cn/d/GT2024/p/13

https://oi.gdcpc.cn/d/GT2024/p/13

X\text{X} 国有 nn 个城市。所有城市被划分为 kk 个区域,每个城市属于一个区域,可能存在空区域。区域用英文字母编号。第一个区域编号为 aa,第二个区域编号为 bb……,第 2626 个区域编号为 zz,第 2727 个区域编号为 A\text{A},第 2828 个区域编号为 B\text{B} …… 保证 k30k\le 30,因此编号总可以用某个小写或大写字母表示。

X\text{X} 国国王 H\text{H} 修建了 mm 条双向的高速公路,保证每个城市恰好在一条高速公路上。如果某条高速公路顺次连接了城市 a1,a2,...,aka_1, a_2, . . . , a_k,沿着高速公路从 aia_iaja_j 需要花费 ij|i - j| 单位时间。

接着,国王 H\text{H} 又在每个区域中修建了一个量子传送器。因此,如果城市 i,ji, j 属于同一区域,沿着传送器从 iijj 只需要花费 11 单位时间。

dis(i,j)dis(i,j) 表示如果可以任意利用高速路与传送器,从城市 ii 到城市 jj 最少需要的时间。请你求出 1i<jndis(i,j)\sum_{1\le i<j\le n} dis(i, j) 的值。输入保证从任意城市能到任意城市。

\(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;
}

posted @ 2024-05-27 08:35  caijianhong  阅读(9)  评论(0编辑  收藏  举报