Two Abbreviations
答案要么是 −1-1−1 要么是 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(i≤j) 时产生贡献的概率,就是 jjj 在 i,i+1,⋯ji, i+1, \cdots ji,i+1,⋯j 中最早被删除的概率,即 1j−i+1\frac{1}{j-i+1}j−i+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 串表示,假设有 xxx 个 010101 , yyy 个 101010 ,zzz 个 000000 和 zzz 个 111111 (显然 000000 和 111111 的个数是相同的),那么我们的要求是:
- 010101 和 111111 后面必须接 010101 或者 000000
- 101010 和 111111 前面必须接 101010 或者 000000
如果 z=0z = 0z=0 ,那么要么 x=nx = nx=n ,要么 y=ny = ny=n 。
否则一定有解,先将 zzz 个 111111 和 z−1z - 1z−1 个 000000 像这样合并成一个 111111 : 11−00−11−00−1111-00-11-00-1111−00−11−00−11。
然后在 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] 内部连,lll 和 rrr 是连通的概率,容斥即可。
#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,⋯ ,axa_1, a_2, \cdots, a_xa1,a2,⋯,ax 和 b1,b2,⋯ ,byb_1, b_2, \cdots, b_yb1,b2,⋯,by ,使得:
- max0<a1<a2<⋯<axmax_0 < a_1 < a_2 < \cdots < a_xmax0<a1<a2<⋯<ax
- max1<b1<b2<⋯<bymax_1 < b_1 < b_2 < \cdots < b_ymax1<b1<b2<⋯<by
- x+max0=y+max1x + max_0 = y + max_1x+max0=y+max1
- 剩下的数里面,所有在原序列中的前缀最大值一定在序列 aaa 或者 bbb 里面。
事实上这些条件是充要的,因为所有不是原序列前缀最大值的数,可以放在前缀最大值所在的序列后面,不产生贡献。
那么,我们可以将 aaa 和 bbb 其中一个调整成全是原序列的前缀最大值。假设 aaa 全是原序列的前缀最大值,那么我们只需要确定 bbb ,然后把剩下的没有分配的原序列前缀最大值安排到 aaa 上面就行了。
假设剩下的数里面原序列前缀最大值个数为 qqq ,有 kkk 个被放到 bbb 里面了,那么有:
cnt0+q−k=cnt1+ycnt_0 + q - k= cnt_1 + ycnt0+q−k=cnt1+y
假设 bbb 里面有 mmm 个不是原来的前缀最大值的数,那么移项变成:
2k+m=cnt0−cnt1+q2k + m = cnt_0 - cnt_1 + q2k+m=cnt0−cnt1+q
注意到右边是定值,所以这个问题变成了找一个上升子序列,每个位置权值是 111 或者 222 ,要凑出某个权值。
显然如果 ccc 能凑出来,那么 c−2c-2c−2 也能凑出来,所以分奇偶倒着维护一个类似最长上升子序列的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 mn≥m ,考虑分治,那么只需要算从一个上面的点到下面的点的贡献。
将上方第 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 ba≤b)。
- 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 ba≤b)。
- 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)≤a≤b≤Right(i,j) ,找到满足这个的使得 iii 最小的点 (p,q)(p,q)(p,q) ,分为两种情况:
- p≤min(Bottom(a),Bottom(b))p\le \min(Bottom(a), Bottom(b))p≤min(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>min(Bottom(a),Bottom(b))p > \min(Bottom(a), Bottom(b))p>min(Bottom(a),Bottom(b)) 时,返回 ∞\infty∞ 。显然。
可以通过预处理 O(1)O(1)O(1) 查询 MeetingPointMeetingPointMeetingPoint 。
再考虑实现 BothReachableBothReachableBothReachable 这个函数,对于 MettingPoint(a,b)>lMettingPoint(a,b) > 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)≤a≤b≤Right(y,x),y≤l 的 (y,x)(y,x)(y,x) 的个数。
如果没有 y≤ly\le ly≤l 这个条件,可以容斥,算:
- 加上 Left(y,x)≤Right(y,x)Left(y,x)\le Right(y,x)Left(y,x)≤Right(y,x) 的个数
- 减去 Left(y,x)>aLeft(y,x) > aLeft(y,x)>a 的个数
- 减去 Right(y,x)<bRight(y,x) < bRight(y,x)<b 的个数
- 加上 a<Left(y,x)≤Right(y,x)<ba < Left(y,x)\le Right(y,x) < ba<Left(y,x)≤Right(y,x)<b 的个数
前三个显然加上 y≤ly\le ly≤l 这个条件也是可以完成的,对于第四个,我们注意到这些东西的 yyy 一定比 MettingPoint(a,b)MettingPoint(a,b)MettingPoint(a,b) 小,所以可以直接忽视 y≤ly\le ly≤l 的条件。
在上述函数的基础上,可以定义 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) 最小的 jjj , R(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')\in SD(1,j′)∈S ,如果存在 D(1,j′′)∈S(j′′>j′)D(1,j'')\in S(j'' > j')D(1,j′′)∈S(j′′>j′) 并且 Bottom(j′)≤Bottom(j′′)Bottom(j')\le Bottom(j'')Bottom(j′)≤Bottom(j′′) ,则 hasjhas_jhasj 不会改变。这是因为如果 jjj 和 j′j'j′ 都能到,那么显然 j′′j''j′′ 也能到。所以,会改变的是一个关于 BottomBottomBottom 的单调栈:假设是 J1,J2,⋯ ,JkJ_1, J_2, \cdots, J_kJ1,J2,⋯,Jk 。hasJkhas_{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<kp < kp<k , hasJphas_{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>p)D(1, J_q)(q > 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)>Bottom(j)Bottom(J_p)> Bottom(j)Bottom(Jp)>Bottom(j) 时,hasq(q<p)has_q(q < 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;
}