Atcoder Grand Contest 028 简要题解

Two Abbreviations

答案要么是 −1-11 要么是 lcm(n,m)lcm(n, m)lcm(n,m)

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  string s, t;
  cin >> n >> m >> s >> t;
  int gcd = __gcd(n, m);
  for (int i = 0, j = 0; i < n && j < m; i += n / gcd, j += m / gcd) {
    if (s[i] != t[j]) {
      cout << -1 << endl;
      return 0;
    }
  }
  cout << (long long)n * m / gcd << endl;
  return 0;
}

Removing Blocks

考虑 iii 在删除 j(i≤j)j(i\le j)j(ij) 时产生贡献的概率,就是 jjji,i+1,⋯ji, i+1, \cdots ji,i+1,j 中最早被删除的概率,即 1j−i+1\frac{1}{j-i+1}ji+11 。当 iii 移动的时候,系数改变是 O(1)O(1)O(1) 的。

#include <bits/stdc++.h>

using namespace std;

const int md = 1e9 + 7;

int add(int x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
  return x;
}

int sub(int x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
  return x;
}

int mul(int x, int y) {
  return (long long)x * y % md;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  cin >> n;
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    cin >> a[i];
  }
  vector<int> inv(n + 1);
  inv[0] = inv[1] = 1;
  for (int i = 2; i <= n; ++i) {
    inv[i] = mul(md - md / i, inv[md % i]);
  }
  int coef = 0;
  for (int i = 1; i <= n; ++i) {
    coef = add(coef, inv[i]);
  }
  int answer = 0;
  for (int i = 0; i < n; ++i) {
    answer = add(answer, mul(coef, a[i]));
    if (i + 1 < n) {
      coef = add(coef, inv[i + 2]);
      coef = sub(coef, inv[n - i]);
    }
  }
  for (int i = 1; i <= n; ++i) {
    answer = mul(answer, i);
  }
  cout << answer << endl;
  return 0;
}

Min Cost Cycle

首先 min⁡(ax,by)\min(a_x, b_y)min(ax,by) 可以认为是 ax+bya_x + b_yax+by 再减去任意一个。那么我们相当于要选出 nnn 个位置,在合法的同时使得它们的和最大。

将每个位置是否被选用 010101 串表示,假设有 xxx010101yyy101010zzz000000zzz111111 (显然 000000111111 的个数是相同的),那么我们的要求是:

  • 010101111111 后面必须接 010101 或者 000000
  • 101010111111 前面必须接 101010 或者 000000

如果 z=0z = 0z=0 ,那么要么 x=nx = nx=n ,要么 y=ny = ny=n

否则一定有解,先将 zzz111111z−1z - 1z1000000 像这样合并成一个 11111111−00−11−00−1111-00-11-00-111100110011

然后在 111111 前面接上所有的 101010 ,后面接上所有的 010101 ,最后用剩下的 000000 把它们串起来。

也就是说,特判掉 z=0z = 0z=0 的情况,我们一定删的数里面一定有两个数属于同一个位置,这个贪心就可以了。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  cin >> n;
  vector<int> a(n), b(n);
  long long answer = 0, sum_a = 0, sum_b = 0;
  vector<pair<int, int>> all(n << 1);
  for (int i = 0; i < n; ++i) {
    cin >> a[i] >> b[i];
    answer += a[i] + b[i];
    sum_a += a[i];
    sum_b += b[i];
    all[i << 1] = make_pair(a[i], i);
    all[i << 1 | 1] = make_pair(b[i], i);
  }
  long long result = max(sum_a, sum_b);
  sort(all.begin(), all.end(), greater<pair<int, int>> ());
  vector<bool> visit(n);
  bool already = false;
  long long sum = 0;
  for (int i = 0; i < n; ++i) {
    if (visit[all[i].second]) {
      already = true;
    }
    sum += all[i].first;
    visit[all[i].second] = true;
  }
  if (already) {
    result = max(result, sum);
  } else {
    for (int i = n; i < n << 1; ++i) {
      if (all[n - 1].second == all[i].second) {
        result = max(result, sum + all[i].first - all[n - 2].first);
      } else {
        result = max(result, sum + all[i].first - all[n - 1].first);
      }
    }
  }
  cout << answer - result << endl;
  return 0;
}

Chords

f(l,r)f(l,r)f(l,r) 表示只在区间 [l,r][l,r][l,r] 内部连,lllrrr 是连通的概率,容斥即可。

#include <bits/stdc++.h>

using namespace std;

const int md = 1e9 + 7;

int add(int x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
  return x;
}

int sub(int x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
  return x;
}

int mul(int x, int y) {
  return (long long)x * y % md;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  cin >> n >> m;
  vector<int> a(m), b(m);
  for (int i = 0; i < m; ++i) {
    cin >> a[i] >> b[i];
    --a[i];
    --b[i];
  }
  vector<int> ways(n + 1);
  ways[0] = 1;
  for (int i = 1; i <= n; ++i) {
    ways[i] = mul(ways[i - 1], (i << 1) - 1);
  }
  vector<vector<int>> f(n << 1, vector<int> (n << 1));
  vector<vector<int>> g(n << 1, vector<int> (n << 1));
  int answer = 0;
  for (int l = (n << 1) - 1; ~l; --l) {
    for (int r = l + 1; r < n << 1; r += 2) {
      bool flag = true;
      int inside = 0;
      for (int i = 0; i < m; ++i) {
        int type = (a[i] >= l && a[i] <= r) + (b[i] >= l && b[i] <= r);
        if (type == 1) {
          flag = false;
          break;
        }
        if (type) {
          ++inside;
        }
      }
      if (flag) {
        inside = (r - l + 1 >> 1) - inside;
        int outside = n - m - inside;
        f[l][r] = g[l][r] = ways[inside];
        for (int i = l + 1; i < r - 1; i += 2) {
          f[l][r] = sub(f[l][r], mul(f[l][i], g[i + 1][r]));
        }
        answer = add(answer, mul(f[l][r], ways[outside]));
      }
    }
  }
  cout << answer << endl;
  return 0;
}

High Elements

直接的想法是按位确定,那么需要判断一个状态是否合法。对于前缀的一个状态,需要记录当前两个序列的最大值 max0,max1max_0, max_1max0,max1 和前缀最大值个数 cnt0,cnt1cnt_0, cnt_1cnt0,cnt1 。对于剩下的数,我们需要安排两个序列 a1,a2,⋯&ThinSpace;,axa_1, a_2, \cdots, a_xa1,a2,,axb1,b2,⋯&ThinSpace;,byb_1, b_2, \cdots, b_yb1,b2,,by ,使得:

  • max0&lt;a1&lt;a2&lt;⋯&lt;axmax_0 &lt; a_1 &lt; a_2 &lt; \cdots &lt; a_xmax0<a1<a2<<ax
  • max1&lt;b1&lt;b2&lt;⋯&lt;bymax_1 &lt; b_1 &lt; b_2 &lt; \cdots &lt; b_ymax1<b1<b2<<by
  • x+max0=y+max1x + max_0 = y + max_1x+max0=y+max1
  • 剩下的数里面,所有在原序列中的前缀最大值一定在序列 aaa 或者 bbb 里面。

事实上这些条件是充要的,因为所有不是原序列前缀最大值的数,可以放在前缀最大值所在的序列后面,不产生贡献。

那么,我们可以将 aaabbb 其中一个调整成全是原序列的前缀最大值。假设 aaa 全是原序列的前缀最大值,那么我们只需要确定 bbb ,然后把剩下的没有分配的原序列前缀最大值安排到 aaa 上面就行了。

假设剩下的数里面原序列前缀最大值个数为 qqq ,有 kkk 个被放到 bbb 里面了,那么有:

cnt0+q−k=cnt1+ycnt_0 + q - k= cnt_1 + ycnt0+qk=cnt1+y

假设 bbb 里面有 mmm 个不是原来的前缀最大值的数,那么移项变成:

2k+m=cnt0−cnt1+q2k + m = cnt_0 - cnt_1 + q2k+m=cnt0cnt1+q

注意到右边是定值,所以这个问题变成了找一个上升子序列,每个位置权值是 111 或者 222 ,要凑出某个权值。

显然如果 ccc 能凑出来,那么 c−2c-2c2 也能凑出来,所以分奇偶倒着维护一个类似最长上升子序列的DP就行了。

#include <bits/stdc++.h>

using namespace std;

const int inf = 0x3f3f3f3f;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  cin >> n;
  vector<int> a(n);
  vector<bool> b(n);
  int prefix = -1, q = 0;
  for (int i = 0; i < n; ++i) {
    cin >> a[i];
    --a[i];
    if (a[i] > prefix) {
      prefix = a[i];
      b[i] = true;
      ++q;
    }
  }
  
  vector<int> fenw_even(n, 0), fenw_odd(n, -inf);
  stack<pair<int*, int>> memory;

  auto modify = [&](vector<int> &fenw, int x, int value) {
    while (x < n) {
      if (fenw[x] < value) {
        memory.emplace(&fenw[x], fenw[x]);
        fenw[x] = value;
      }
      x |= x + 1;
    }
  };

  auto query = [&](vector<int> &fenw, int x) {
    int result = -inf;
    while (x >= 0) {
      result = max(result, fenw[x]);
      x = (x & x + 1) - 1;
    }
    return result;
  };

  vector<int> size(n);
  for (int i = n - 1; ~i; --i) {
    int even = -inf, odd = -inf;
    size[i] = memory.size();
    if (b[i]) {
      even = query(fenw_even, n - a[i] - 1) + 2;
      odd = query(fenw_odd, n - a[i] - 1) + 2;
    } else {
      even = query(fenw_odd, n - a[i] - 1) + 1;
      odd = query(fenw_even, n - a[i] - 1) + 1;
    }
    if (even >= 0) {
      modify(fenw_even, n - a[i] - 1, even);
    }
    if (odd >= 0) {
      modify(fenw_odd, n - a[i] - 1, odd);
    }
  }

  auto check = [&](int x, int need) {
    if (need < 0) {
      return false;
    }
    if (need & 1) {
      return query(fenw_odd, n - x - 1) >= need;
    } else {
      return query(fenw_even, n - x - 1) >= need;
    }
  };

  auto valid = [&](int max0, int max1, int diff, int q) {
    return check(max0, q - diff) || check(max1, q + diff);
  };

  int max0 = 0, max1 = 0;
  if (!check(0, q)) {
    puts("-1");
    return 0;
  }
  int diff = 0;
  for (int i = 0; i < n; ++i) {
    if (b[i]) {
      --q;
    }
    while (memory.size() > size[i]) {
      *memory.top().first = memory.top().second;
      memory.pop();
    }
    if (max0 <= a[i]) {
      if (valid(a[i], max1, diff + 1, q)) {
        cout << 0;
        max0 = a[i];
        ++diff;
      } else {
        cout << 1;
        if (max1 <= a[i]) {
          max1 = a[i];
          --diff;
        }
      }
    } else {
      if (valid(max0, max1, diff, q)) {
        cout << 0;
      } else {
        cout << 1;
        if (max1 <= a[i]) {
          max1 = a[i];
          --diff;
        }
      }
    }
  }
  cout << endl;

  return 0;
}

Reachable Cells

假设 n≥mn\ge mnm ,考虑分治,那么只需要算从一个上面的点到下面的点的贡献。

将上方第 iii 行第 jjj 列的数记为 U(i,j)U(i, j)U(i,j) ,下方的记为 D(i,j)D(i,j)D(i,j) ,假设上方有 HUH_UHU 行,下方有 HDH_DHD 行。为了方便这里不考虑带权。

定义:

  • MeetingPoint(a,b)MeetingPoint(a,b)MeetingPoint(a,b) 表示 D(1,a)D(1,a)D(1,a)D(1,b)D(1,b)D(1,b) 最早在哪行能够同时到达(a≤ba\le bab)。
  • BothReachable(a,b,l)BothReachable(a,b,l)BothReachable(a,b,l) 表示 D(1,a)D(1,a)D(1,a)D(1,b)D(1,b)D(1,b) 在前 lll 行同时能到达的点数(a≤ba\le bab)。
  • Left(i,j)Left(i,j)Left(i,j) 表示最小的 xxx 使得 D(1,x)D(1,x)D(1,x) 可以到 D(i,j)D(i,j)D(i,j)
  • Right(i,j)Right(i,j)Right(i,j) 表示最大的 xxx 使得 D(1,x)D(1,x)D(1,x) 可以到 D(i,j)D(i,j)D(i,j)
  • Top(j)Top(j)Top(j) 表示最小的 yyy 使得 U(y,x)U(y,x)U(y,x) 可以到 D(1,j)D(1, j)D(1,j)
  • Bottom(j)Bottom(j)Bottom(j) 表示最大的 yyy 使得 D(1,j)D(1,j)D(1,j) 可以到 D(y,x)D(y,x)D(y,x)

考虑实现 MeetingPointMeetingPointMeetingPoint 这个函数,那么显然有 Left(i,j)≤a≤b≤Right(i,j)Left(i, j)\le a\le b\le Right(i,j)Left(i,j)abRight(i,j) ,找到满足这个的使得 iii 最小的点 (p,q)(p,q)(p,q) ,分为两种情况:

  • p≤min⁡(Bottom(a),Bottom(b))p\le \min(Bottom(a), Bottom(b))pmin(Bottom(a),Bottom(b)) 时,返回 ppp 。考虑 Left(i,j),Right(i,j),Bottom(a),Bottom(b)Left(i,j), Right(i,j), Bottom(a), Bottom(b)Left(i,j),Right(i,j),Bottom(a),Bottom(b) 构成的路径,易证。
  • p&gt;min⁡(Bottom(a),Bottom(b))p &gt; \min(Bottom(a), Bottom(b))p>min(Bottom(a),Bottom(b)) 时,返回 ∞\infty 。显然。

可以通过预处理 O(1)O(1)O(1) 查询 MeetingPointMeetingPointMeetingPoint

再考虑实现 BothReachableBothReachableBothReachable 这个函数,对于 MettingPoint(a,b)&gt;lMettingPoint(a,b) &gt; lMettingPoint(a,b)>l 的情况,直接返回 000

其他情况,相当于数 Left(y,x)≤a≤b≤Right(y,x),y≤lLeft(y,x)\le a\le b\le Right(y,x), y\le lLeft(y,x)abRight(y,x),yl(y,x)(y,x)(y,x) 的个数。

如果没有 y≤ly\le lyl 这个条件,可以容斥,算:

  • 加上 Left(y,x)≤Right(y,x)Left(y,x)\le Right(y,x)Left(y,x)Right(y,x) 的个数
  • 减去 Left(y,x)&gt;aLeft(y,x) &gt; aLeft(y,x)>a 的个数
  • 减去 Right(y,x)&lt;bRight(y,x) &lt; bRight(y,x)<b 的个数
  • 加上 a&lt;Left(y,x)≤Right(y,x)&lt;ba &lt; Left(y,x)\le Right(y,x) &lt; ba<Left(y,x)Right(y,x)<b 的个数

前三个显然加上 y≤ly\le lyl 这个条件也是可以完成的,对于第四个,我们注意到这些东西的 yyy 一定比 MettingPoint(a,b)MettingPoint(a,b)MettingPoint(a,b) 小,所以可以直接忽视 y≤ly\le lyl 的条件。

在上述函数的基础上,可以定义 Reachable(a)Reachable(a)Reachable(a) 表示 D(1,a)D(1,a)D(1,a) 能到达的位置个数。

枚举 yyy ,定义 L(x)L(x)L(x) 表示 U(y,x)U(y,x)U(y,x) 能到达 U(Hu,j)U(H_u, j)U(Hu,j) 最小的 jjjR(x)R(x)R(x) 表示最大的 jjj 。那么 L(x),R(x)L(x), R(x)L(x),R(x) 都是单调不降的。问题变成:维护一个集合,支持插入 D(1,j)D(1,j)D(1,j) ,删除 D(1,j)D(1,j)D(1,j) ,询问当前集合内能到达的点个数。保证插入的 jjj 是当前最大值,删除的 jjj 是当前最小值。

定义 hasjhas_jhasj 表示 D(1,j)D(1,j)D(1,j) 能到达的,并且集合中大于 jjj 的任何位置都不能到达的位置个数。注意到删除操作是不会改变 hasjhas_jhasj 的,所以只考虑插入操作。对于 D(1,j′)∈SD(1, j&#x27;)\in SD(1,j)S ,如果存在 D(1,j′′)∈S(j′′&gt;j′)D(1,j&#x27;&#x27;)\in S(j&#x27;&#x27; &gt; j&#x27;)D(1,j)S(j>j) 并且 Bottom(j′)≤Bottom(j′′)Bottom(j&#x27;)\le Bottom(j&#x27;&#x27;)Bottom(j)Bottom(j) ,则 hasjhas_jhasj 不会改变。这是因为如果 jjjj′j&#x27;j 都能到,那么显然 j′′j&#x27;&#x27;j 也能到。所以,会改变的是一个关于 BottomBottomBottom 的单调栈:假设是 J1,J2,⋯&ThinSpace;,JkJ_1, J_2, \cdots, J_kJ1,J2,,JkhasJkhas_{J_k}hasJk 会减去 BothReachable(Jk,j,min⁡(Bottom(Jk),Bottom(j)))BothReachable(J_k, j, \min(Bottom(J_k), Bottom(j)))BothReachable(Jk,j,min(Bottom(Jk),Bottom(j))) 。而对于 p&lt;kp &lt; kp<khasJphas_{J_p}hasJp 会减去 D(1,j)D(1,j)D(1,j)D(1,Jp)D(1, J_p)D(1,Jp) 都能到,但 D(1,Jq)(q&gt;p)D(1, J_q)(q &gt; p)D(1,Jq)(q>p) 不能到的格子个数。这个值是 BothReachable(Jp,j,min⁡(Bottom(Jp),Bottom(j)))−BothReachable(Jp,j,min⁡(Bottom(Jp+1,j)))BothReachable(J_p, j, \min(Bottom(J_p), Bottom(j))) - BothReachable(J_p, j, \min(Bottom(J_{p+1}, j)))BothReachable(Jp,j,min(Bottom(Jp),Bottom(j)))BothReachable(Jp,j,min(Bottom(Jp+1,j)))

。注意到当 Bottom(Jp)&gt;Bottom(j)Bottom(J_p)&gt; Bottom(j)Bottom(Jp)>Bottom(j) 时,hasq(q&lt;p)has_q(q &lt; p)hasq(q<p) 不变,所以可以直接用单调栈来更新。

#include <bits/stdc++.h>

using namespace std;

const int inf = 0x3f3f3f3f;

void cmax(int &x, int y) {
  if (x < y) {
    x = y;
  }
}

void cmin(int &x, int y) {
  if (x > y) {
    x = y;
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  cin >> n;
  vector<string> board(n);
  for (int i = 0; i < n; ++i) {
    cin >> board[i];
  }
  long long answer = 0;

  function<void(vector<string>)> solve = [&](vector<string> board) {
    int n = board.size(), m = board[0].size();
    
    if (n < m) {
      vector<string> rotated(m, string(n, ' '));
      for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
          rotated[j][i] = board[i][j];
        }
      }
      swap(n, m);
      board = rotated;
    }
    
    if (m == 1) {
      int sum = 0;
      for (int i = 0; i < n; ++i) {
        if (board[i][0] == '#') {
          sum = 0;
        } else {
          answer += (board[i][0] - '0') * sum;
          sum += board[i][0] - '0';
        }
      }
      return;
    }

    vector<string> u = vector<string> (board.begin(), board.begin() + (n >> 1));
    vector<string> d = vector<string> (board.begin() + (n >> 1), board.end());
    solve(u);
    solve(d);
    u.push_back(d[0]);

    int nu = n >> 1, nd = n + 1 >> 1;
    vector<vector<int>> left(nd, vector<int> (m, inf));
    vector<vector<int>> right(nd, vector<int> (m, -inf));
    vector<int> top(m);
    vector<int> bottom(m);
    for (int i = 0; i < m; ++i) {
      if (d[0][i] != '#') {
        left[0][i] = right[0][i] = i;
      }
    }
    for (int i = 0; i < nd; ++i) {
      for (int j = 0; j < m; ++j) {
        if (d[i][j] != '#') {
          if (i) {
            cmin(left[i][j], left[i - 1][j]);
            cmax(right[i][j], right[i - 1][j]);
          }
          if (j) {
            cmin(left[i][j], left[i][j - 1]);
            cmax(right[i][j], right[i][j - 1]);
          }
        }
      }
    }

    {
      vector<vector<int>> temp(nd, vector<int> (m, -inf));
      for (int i = nd - 1; ~i; --i) {
        for (int j = m - 1; ~j; --j) {
          if (d[i][j] != '#') {
            temp[i][j] = i;
            if (i + 1 < nd) {
              cmax(temp[i][j], temp[i + 1][j]);
            }
            if (j + 1 < m) {
              cmax(temp[i][j], temp[i][j + 1]);
            }
          }
        }
      }
      for (int i = 0; i < m; ++i) {
        bottom[i] = temp[0][i];
      }
    }

    {
      vector<vector<int>> temp(nu + 1, vector<int> (m, inf));
      for (int i = 0; i <= nu; ++i) {
        for (int j = 0; j < m; ++j) {
          if (u[i][j] != '#') {
            temp[i][j] = i;
            if (i) {
              cmin(temp[i][j], temp[i - 1][j]);
            }
            if (j) {
              cmin(temp[i][j], temp[i][j - 1]);
            }
          }
        }
      }
      for (int i = 0; i < m; ++i) {
        top[i] = temp[nu][i];
      }
    }

    vector<vector<int>> mp(m, vector<int> (m, inf));
    for (int i = 0; i < nd; ++i) {
      for (int j = 0; j < m; ++j) {
        if (left[i][j] <= right[i][j]) {
          cmin(mp[left[i][j]][right[i][j]], i);
        }
      }
    }
    for (int l = 0; l < m; ++l) {
      for (int r = m - 1; ~r; --r) {
        if (l) {
          cmin(mp[l][r], mp[l - 1][r]);
        }
        if (r + 1 < m) {
          cmin(mp[l][r], mp[l][r + 1]);
        }
      }
    }

    auto meeting_point = [&](int a, int b) {
      int p = mp[a][b];
      return p <= min(bottom[a], bottom[b]) ? p : inf;
    };

    vector<int> sum1(nd);
    vector<vector<int>> sum2(nd, vector<int> (m));
    vector<vector<int>> sum3(nd, vector<int> (m));
    vector<vector<int>> sum4(m, vector<int> (m));

    for (int i = 0; i < nd; ++i) {
      if (i) {
        sum1[i] = sum1[i - 1];
        for (int j = 0; j < m; ++j) {
          sum2[i][j] = sum2[i - 1][j];
          sum3[i][j] = sum3[i - 1][j];
        }
      }
      for (int j = 0; j < m; ++j) {
        if (left[i][j] <= right[i][j]) {
          sum1[i] += d[i][j] - '0';
          if (left[i][j]) {
            sum2[i][left[i][j] - 1] += d[i][j] - '0';
          }
          if (right[i][j] + 1 < m) {
            sum3[i][right[i][j] + 1] += d[i][j] - '0';
          }
          if (left[i][j] && right[i][j] + 1 < m) {
            sum4[left[i][j] - 1][right[i][j] + 1] += d[i][j] - '0';
          }
        }
      }
    }
    for (int i = 0; i < nd; ++i) {
      for (int j = m - 1; j; --j) {
        sum2[i][j - 1] += sum2[i][j];
      }
      for (int j = 1; j < m; ++j) {
        sum3[i][j] += sum3[i][j - 1];
      }
    }
    for (int l = m - 1; ~l; --l) {
      for (int r = l; r < m; ++r) {
        if (l + 1 < m) {
          sum4[l][r] += sum4[l + 1][r];
        }
        if (r) {
          sum4[l][r] += sum4[l][r - 1];
        }
        if (l + 1 < m && r) {
          sum4[l][r] -= sum4[l + 1][r - 1];
        }
      }
    }

    auto both_reachable = [&](int a, int b, int l) {
      if (meeting_point(a, b) > l) {
        return 0;
      } else {
        return sum1[l] - sum2[l][a] - sum3[l][b] + sum4[a][b];
      }
    };

    vector<int> reachable(m);
    for (int i = 0; i < m; ++i) {
      reachable[i] = both_reachable(i, i, bottom[i]);
    }

    vector<vector<int>> event(nu);
    vector<bool> ban(m);
    for (int i = 0; i < m; ++i) {
      if (top[i] >= nu) {
        ban[i] = true;
      } else {
        event[top[i]].push_back(i);
      }
    }
    vector<int> l(m), r(m);
    for (int i = m - 1; ~i; --i) {
      if (d[0][i] == '#') {
        l[i] = inf;
        r[i] = -inf;
      } else {
        l[i] = i;
        r[i] = max(i, i + 1 < m ? r[i + 1] : -inf);
      }
    }
    for (int row = nu - 1; ~row; --row) {
      for (int i = m - 1; ~i; --i) {
        if (u[row][i] == '#') {
          l[i] = inf;
          r[i] = -inf;
        } else if (i + 1 < m) {
          cmin(l[i], l[i + 1]);
          cmax(r[i], r[i + 1]);
        }
      }
      vector<int> has(m);
      vector<int> st(m);
      int sum = 0, stl = 0, str = 0, myl = 0, myr = -1;
      
      auto insert = [&](int x) {
        if (!ban[x]) {
          if (stl < str) {
            sum -= has[st[str - 1]];
            has[st[str - 1]] -= both_reachable(st[str - 1], x, min(bottom[st[str - 1]], bottom[x]));
            sum += has[st[str - 1]];
            if (bottom[st[str - 1]] <= bottom[x]) {
              --str;
              while (stl < str) {
                sum -= has[st[str - 1]];
                has[st[str - 1]] -= both_reachable(st[str - 1], x, min(bottom[st[str - 1]], bottom[x])) - both_reachable(st[str - 1], x, min(bottom[st[str]], bottom[x]));
                sum += has[st[str - 1]];
                if (bottom[st[str - 1]] <= bottom[x]) {
                  --str;
                } else {
                  break;
                }
              }
            }
          }
          st[str++] = x;
          has[x] = reachable[x];
          sum += has[x];
        }
      };

      auto erase = [&](int x) {
        if (!ban[x]) {
          sum -= has[x];
          if (stl < str && st[stl] == x) {
            ++stl;
          }
        }
      };

      for (int i = 0; i < m; ++i) {
        if (l[i] <= r[i]) {
          while (myr < r[i]) {
            insert(++myr);
          }
          while (myl < l[i]) {
            erase(myl++);
          }
          answer += (u[row][i] - '0') * sum;
        }
      }
      for (auto p : event[row]) {
        ban[p] = true;
      }
    }
  };

  solve(board);
  cout << answer << endl;
  return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值