对所有的 #
编号并构造图:若 \(i\) 被“影响”后能影响 \(j\),则对 \(i\) 和 \(j\) 连边。对于同一列的点,可以只对相邻的点连边。
先找出原图中的所有极大强连通分量并缩点。对于 Easy Version,答案即为缩点图中入度为 \(0\) 的点数。
对于 Hard Version,若 \(a_i > 0\),将第 \(i\) 列的第 \(a_i\) 个点标记为关键点。我们需要选出最少的点,使得它们可以到达所有关键点。
设关键点的集合为 \(S\),若存在 \(u, v \in S\),\(u\) 可达 \(v\),那么将 \(v\) 从 \(S\) 中删除。
有结论:每个点可以覆盖的存在关键点的列形成连续区间。
证明:不失一般性地,设从左到右存在关键点 \(u, v, w \in S\),\(u\) 可达 \(w\),但不可达 \(v\)。那么 \(u\) 可经过 \(v\) 列中的某点 \(i\),然后到达 \(w\),因为 \(v\) 的关键点高于 \(i\)(否则可以到达 \(v\)),所以 \(v\) 可达 \(w\),故 \(w \notin S\)。
那么通过一次 DAG 上 DP 求出所有关键点和每个点对应的区间,对区间左端点排序后贪心即可。
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <numeric>
#include <vector>
#include <cassert>
using namespace std;
#define LOG(f...) fprintf(stderr, f)
#define all(cont) begin(cont), end(cont)
using ll = long long;
template<class T> void read(T &x) {
char ch; x = 0;
int f = 1;
while (isspace(ch = getchar()));
if (ch == '-') ch = getchar(), f = -1;
do x = x * 10 + (ch - '0'); while(isdigit(ch = getchar()));
x *= f;
}
template<class T, class ...A> void read(T &x, A&... args) { read(x); read(args...); }
const int N = 400005;
vector<int> pos[N];
char s[N];
int pre_id[N];
int a[N];
int dfn[N], low[N], scc[N], dfc = 0, stk[N], top = 0, scc_cnt;
bool instk[N];
vector<int> G[N], RG[N];
int r_ind[N];
bool key[N], vis[N];
int key_id[N], key_cnt;
struct range {
int l, r;
bool operator<(const range &r) const { return l < r.l; }
} ran[N];
void tarjan(int x) {
dfn[x] = low[x] = ++dfc;
stk[++top] = x; instk[x] = true;
for (int v : G[x])
if (!dfn[v]) tarjan(v), low[x] = min(low[x], low[v]);
else if (instk[v]) low[x] = min(low[x], dfn[v]);
if (low[x] == dfn[x]) {
int tp;
do {
tp = stk[top--];
instk[tp] = false;
scc[tp] = scc_cnt;
} while (tp != x);
++scc_cnt;
}
}
void scc_dp() {
vector<int> ord;
for (int i = 0; i < scc_cnt; ++i)
if (!r_ind[i]) ord.push_back(i);
for (int i = 0; i < scc_cnt; ++i) {
int x = ord[i];
if (vis[x]) key[x] = false;
if (key[x]) vis[x] = true;
for (int v : RG[x]) {
--r_ind[v];
if (vis[x] || key[x]) vis[v] = true;
if (!r_ind[v]) ord.push_back(v);
}
}
for (int i = 0; i < scc_cnt; ++i)
if (key[i]) key_id[i] = key_cnt++;
for (auto i = rbegin(ord); i != rend(ord); ++i) {
int x = *i;
ran[x].l = key[x] ? key_id[x] : numeric_limits<int>::max();
ran[x].r = key[x] ? key_id[x] : numeric_limits<int>::min();
for (int v : RG[x]) {
ran[x].l = min(ran[x].l, ran[v].l);
ran[x].r = max(ran[x].r, ran[v].r);
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
int n, m;
read(n, m);
for (int i = 0; i < n; ++i) {
scanf("%s", s);
for (int j = 0; j < m; ++j)
if (s[j] == '#') pos[j].push_back(n - 1 - i);
}
for (int i = 0; i < m; ++i)
read(a[i]);
for (int i = 0; i < m; ++i)
reverse(all(pos[i]));
pre_id[0] = 0;
for (int i = 1; i <= m; ++i)
pre_id[i] = pre_id[i - 1] + pos[i - 1].size();
for (int i = 0; i < m; ++i)
for (auto it = begin(pos[i]); it != end(pos[i]); ++it) {
int id = pre_id[i] + (it - begin(pos[i]));
if (it != begin(pos[i])) G[id].push_back(id - 1);
if (next(it) != end(pos[i]) && it[1] == *it + 1) G[id].push_back(id + 1);
if (i != 0) {
auto t = upper_bound(all(pos[i - 1]), *it);
if (t != begin(pos[i - 1])) G[id].push_back(pre_id[i - 1] - 1 + (t - begin(pos[i - 1])));
}
if (i + 1 != m) {
auto t = upper_bound(all(pos[i + 1]), *it);
if (t != begin(pos[i + 1])) G[id].push_back(pre_id[i + 1] - 1 + (t - begin(pos[i + 1])));
}
}
int nc = pre_id[m];
for (int i = 0; i < nc; ++i)
if (!dfn[i]) tarjan(i);
for (int i = 0; i < m; ++i)
if (a[i])
key[scc[pre_id[i] + a[i] - 1]] = true;
for (int i = 0; i < nc; ++i)
for (int v : G[i])
if (scc[i] != scc[v]) RG[scc[i]].push_back(scc[v]), ++r_ind[scc[v]];
scc_dp();
sort(ran, ran + scc_cnt);
int max_r = -1, nmax_r = 0, res = 0;
for (int i = 0; i < scc_cnt && max_r + 1 < key_cnt; ++i) {
nmax_r = max(nmax_r, ran[i].r);
if (i + 1 == scc_cnt || ran[i + 1].l > max_r + 1)
++res, max_r = nmax_r;
}
printf("%d\n", res);
return 0;
}