XXII Open Cup named after E.V. Pankratiev, Grand Prix of Daejeon【杂题】
省流:A、B、C、E、H、K、L。
D 和 I 之后可能会看心情补,剩下的题大概这辈子都不会做了。
A. Points
有两个由二维平面上的点组成的可重点集
- 当
中存在一个为空时, 。 - 否则
。
初始时
先讲个一开始想的愚笨做法:线段树分治一下,每次加入元素
考虑把
我们使用线段树来维护答案,每个节点存区间内
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int, int> pi;
#define fi first
#define se second
constexpr int N = 5e5 + 5, inf = 1e9;
bool Mbe;
int n = 250000, q;
multiset <int> sux[N], suy[N], svx[N], svy[N];
#define m ((l + r) >> 1)
int tr[N << 2], ux[N << 2], uy[N << 2], vx[N << 2], vy[N << 2];
void push_up(int x) {
tr[x] = min(tr[x << 1], tr[x << 1 | 1]);
int val = min(uy[x << 1] + vy[x << 1 | 1], ux[x << 1 | 1] + vx[x << 1]);
tr[x] = min(tr[x], val);
ux[x] = min(ux[x << 1], ux[x << 1 | 1]);
uy[x] = min(uy[x << 1], uy[x << 1 | 1]);
vx[x] = min(vx[x << 1], vx[x << 1 | 1]);
vy[x] = min(vy[x << 1], vy[x << 1 | 1]);
}
void build(int x, int l, int r) {
tr[x] = ux[x] = uy[x] = vx[x] = vy[x] = inf;
if (l == r) return;
build(x << 1, l, m), build(x << 1 | 1, m + 1, r);
}
pi cur;
void mdf(int x, int l, int r, int p, int v, int ty) { // v1 ins v2 del ty1 u ty2 v
if (l == r) {
int pos = l + n, nx = cur.fi, ny = cur.se;
assert(pos >= 0);
if (v == 1) {
if (ty == 1) {
sux[pos].insert(nx);
suy[pos].insert(ny);
ux[x] = *sux[pos].begin();
uy[x] = *suy[pos].begin();
} else {
svx[pos].insert(nx);
svy[pos].insert(ny);
vx[x] = *svx[pos].begin();
vy[x] = *svy[pos].begin();
}
} else {
if (ty == 1) {
auto it = sux[pos].find(nx);
sux[pos].erase(it);
it = suy[pos].find(ny);
suy[pos].erase(it);
if (sux[pos].empty()) ux[x] = inf;
else ux[x] = *sux[pos].begin();
if (suy[pos].empty()) uy[x] = inf;
else uy[x] = *suy[pos].begin();
} else {
auto it = svx[pos].find(nx);
svx[pos].erase(it);
it = svy[pos].find(ny);
svy[pos].erase(it);
if (svx[pos].empty()) vx[x] = inf;
else vx[x] = *svx[pos].begin();
if (svy[pos].empty()) vy[x] = inf;
else vy[x] = *svy[pos].begin();
}
}
tr[x] = min(ux[x] + vx[x], uy[x] + vy[x]);
return;
}
if (p <= m) mdf(x << 1, l, m, p, v, ty);
else mdf(x << 1 | 1, m + 1, r, p, v, ty);
push_up(x);
}
#undef m
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> q;
build(1, -n, n);
while (q--) {
int op, s, x, y;
cin >> op >> s >> x >> y;
cur = make_pair(x, y);
if (s == 1) mdf(1, -n, n, x - y, op, s);
else mdf(1, -n, n, y - x, op, s);
if (tr[1] >= inf) cout << "-1\n";
else cout << tr[1] << "\n";
}
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << "ms\n";
return 0;
}
/*
6
1 1 100 100
1 2 30 130
1 1 120 170
2 1 100 100
1 2 70 100
2 1 120 170
*/
B. Bingo
构造一个 #
,且不存在任意一行、一列或一条对角线上全是 #
,或报告无解。
显然,我们只需要求出最多能放多少个 #
并构造方案。
-
当
为奇数时, 的最大值为 ,留出一条对角线即可。 -
当
为偶数时:- 若
,则 的最大值为 。 - 否则有
,此时 的最大值也是 ,留出一条对角线,再把最中间 的方阵旋转一下即可。
- 若
时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int, int> pi;
#define fi first
#define se second
constexpr int N = 1e2 + 5, mod = 1e9 + 7;
bool Mbe;
int n, k;
char a[N][N];
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
if (n == 1) {
if (k == 0) cout << "YES\n.\n";
else cout << "NO\n";
return 0;
} if (n == 2) {
if (k == 0) cout << "YES\n..\n..\n";
if (k == 1) cout << "YES\n#.\n..\n";
if (k >= 2) cout << "NO\n";
return 0;
}
if (k > n * n - n) {
cout << "NO\n";
return 0;
}
cout << "YES\n";
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
a[i][j] = '.';
int c = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (n % 2 == 1) {
if (i != j) {
if (c == k) goto out;
a[i][j] = '#';
c++;
}
} else {
if (i != j) {
if (i == n / 2 && j == n / 2 + 1) continue;
if (i == n / 2 + 1 && j == n / 2) continue;
if (c == k) goto out;
a[i][j] = '#';
c++;
} else if (i == n / 2 || i == n / 2 + 1) {
if (c == k) goto out;
a[i][j] = '#';
c++;
}
}
}
out :
for (int i = 1; i <= n; i++) cout << a[i] + 1 << "\n";
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << "ms\n";
return 0;
}
C. AND PLUS OR
给定一个长为
考虑改写一下式子,将
当
反复上述操作,最后一定能够得到一个
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int, int> pi;
#define fi first
#define se second
constexpr int N = 2e6 + 5, inf = 1e9;
bool Mbe;
int n, a[N];
void solve() {
cin >> n;
for (int i = 0; i < 1 << n; i++) cin >> a[i];
int x = -1, y = 0;
for (int s = 0; s < 1 << n; s++)
for (int i = 0; i < n; i++)
if (!((s >> i) & 1))
for (int j = 0; j < n; j++)
if ((i != j) && !((s >> j) & 1)) {
int v1 = a[s | (1 << i)] + a[s | (1 << j)];
int v2 = a[s] + a[s | (1 << i) | (1 << j)];
if (v1 < v2) {
x = s | (1 << i), y = s | (1 << j);
goto end;
}
}
end :
if (x == -1) cout << "-1\n";
else cout << x << " " << y << "\n";
}
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t; t = 1;
while (t--) solve();
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << "ms\n";
return 0;
}
完全不理解为什么场上过这题的队比过 A 的多那么多,难道大家都是猜结论大师吗。
E. Yet Another Interval Graph Problem
给定
考虑 DP。但我们发现删点难以处理,改为考虑保留哪些点并最大化价值。显然每个连通块会形成一个区间,设
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int, int> pi;
#define fi first
#define se second
constexpr int N = 5e5 + 5, inf = 1e9;
int n, k; LL ans, f[N];
struct seg {
int l, r, val;
};
vector <int> t;
vector <seg> tmp, add[N], del[N];
LL sum;
priority_queue <int> q;
void ins(int x) {
if (q.size() < k) {
sum += x;
q.push(-x);
} else if (-q.top() < x) {
sum -= -q.top();
sum += x;
q.pop();
q.push(-x);
}
}
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
int l, r, w;
cin >> l >> r >> w;
t.emplace_back(l);
t.emplace_back(r);
ans += w;
tmp.emplace_back((seg){ l, r, w });
}
sort(t.begin(), t.end());
t.erase(unique(t.begin(), t.end()), t.end());
for (auto it : tmp) {
it.l = lower_bound(t.begin(), t.end(), it.l) - t.begin() + 1;
it.r = lower_bound(t.begin(), t.end(), it.r) - t.begin() + 1;
// cerr << it.l << " " << it.r << "\n";
add[it.r].emplace_back(it);
}
n = t.size();
for (int i = 1; i <= n; i++) {
sum = 0;
while (q.size()) q.pop();
for (auto it : add[i]) del[it.l].emplace_back(it);
for (int j = i; j >= 1; j--) {
for (auto it : del[j]) ins(it.val);
f[i] = max(f[i], f[j - 1] + sum);
}
}
ans -= f[n];
cout << ans << "\n";
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << "ms\n";
return 0;
}
/*
5 2
1 4 1
3 6 2
5 8 5
7 10 2
9 12 1
*/
H. Endless Road
给定
- 设区间
当前的权值 为区间内未被染色的位置数量。找到 最小的区间 (若有多个则取编号最小的一个)。 - 将区间
内的所有位置染色。
请你依次输出每轮选择的区间编号。
先离散化一下,顺便把区间改成闭区间。这样每个位置带权但是只有
对于两个区间
考虑直接维护每个区间的权值
接下来考虑相互包含的情况如何处理。此时我们需要在删除区间
实现细节见代码。总时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int, int> pi;
#define fi first
#define se second
constexpr int N = 5e5 + 5, inf = 2e9;
bool Mbe;
int n, l[N], r[N], m, b[N];
vector <int> tmp;
struct BIT {
int c[N];
void add(int x, int v) {
for (int i = x; i <= m; i += i & -i) c[i] += v;
}
int qry(int x) {
int res = 0;
for (int i = x; i; i -= i & -i) res += c[i];
return res;
}
int qry(int l, int r) {
return qry(r) - qry(l - 1);
}
} bit;
set <pi> s[N], curL, curR;
set <int> rest;
struct SGT1 {
#define m ((l + r) >> 1)
int mi[N << 2], id[N << 2];
void build(int x, int l, int r) {
mi[x] = inf;
if (l == r) return;
build(x << 1, l, m), build(x << 1 | 1, m + 1, r);
}
void push_up(int x) {
if (mi[x << 1] < mi[x << 1 | 1]) mi[x] = mi[x << 1], id[x] = id[x << 1];
else mi[x] = mi[x << 1 | 1], id[x] = id[x << 1 | 1];
}
void upd(int x, int l, int r, int p, pi v) {
if (l == r) return mi[x] = v.fi, id[x] = v.se, void();
if (p <= m) upd(x << 1, l, m, p, v);
else upd(x << 1 | 1, m + 1, r, p, v);
push_up(x);
}
pi qry(int x, int l, int r, int ql, int qr) {
if (ql > qr) return make_pair(0, 0);
if (ql <= l && qr >= r) return make_pair(mi[x], id[x]);
if (qr <= m) return qry(x << 1, l, m, ql, qr);
if (qr > m) return qry(x << 1 | 1, m + 1, r, ql, qr);
pi p = qry(x << 1, l, m, ql, qr);
pi q = qry(x << 1 | 1, m + 1, r, ql, qr);
return p.fi < q.fi ? p : q;
}
#undef m
} sgt1;
struct SGT2 {
#define m ((l + r) >> 1)
int mi[N << 2], id[N << 2], tag[N << 2];
void build(int x, int l, int r) {
mi[x] = inf;
if (l == r) return;
build(x << 1, l, m), build(x << 1 | 1, m + 1, r);
}
void push_up(int x) {
if (mi[x << 1] == mi[x << 1 | 1]) {
mi[x] = mi[x << 1];
id[x] = min(id[x << 1], id[x << 1 | 1]);
} else {
if (mi[x << 1] < mi[x << 1 | 1]) mi[x] = mi[x << 1], id[x] = id[x << 1];
else mi[x] = mi[x << 1 | 1], id[x] = id[x << 1 | 1];
}
}
void push_tag(int x, int v) {
mi[x] += v;
tag[x] += v;
}
void push_down(int x) {
if (tag[x]) push_tag(x << 1, tag[x]), push_tag(x << 1 | 1, tag[x]), tag[x] = 0;
}
void upd(int x, int l, int r, int p, pi v) {
if (l == r) return mi[x] = v.fi, id[x] = v.se, void();
push_down(x);
if (p <= m) upd(x << 1, l, m, p, v);
else upd(x << 1 | 1, m + 1, r, p, v);
push_up(x);
}
void mdf(int x, int l, int r, int ql, int qr, int v) {
if (ql > qr) return;
if (ql <= l && qr >= r) return push_tag(x, v);
push_down(x);
if (ql <= m) mdf(x << 1, l, m, ql, qr, v);
if (qr > m) mdf(x << 1 | 1, m + 1, r, ql, qr, v);
push_up(x);
}
#undef m
} sgt2;
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> l[i] >> r[i];
tmp.emplace_back(l[i]);
tmp.emplace_back(r[i]);
}
sort(tmp.begin(), tmp.end());
tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end());
static int suf[N];
m = tmp.size() - 1;
for (int i = 1; i <= m; i++) b[i] = tmp[i] - tmp[i - 1];
for (int i = 1; i <= n; i++) {
l[i] = lower_bound(tmp.begin(), tmp.end(), l[i]) - tmp.begin();
r[i] = lower_bound(tmp.begin(), tmp.end(), r[i]) - tmp.begin();
l[i]++;
}
// for (int i = 1; i <= m; i++) cout << b[i] << " \n"[i == m];
// for (int i = 1; i <= n; i++) cout << "["<< l[i] << ", " << r[i] << "]\n";
for (int i = 1; i <= n; i++) s[l[i]].insert(make_pair(r[i], i));
for (int i = 1; i <= m; i++) rest.insert(i);
l[0] = r[0] = 0;
l[n + 1] = r[n + 1] = m + 1;
curL.insert(make_pair(0, 0));
curR.insert(make_pair(0, 0));
curL.insert(make_pair(m + 1, n + 1));
curR.insert(make_pair(m + 1, n + 1));
sgt1.build(1, 1, m);
sgt2.build(1, 1, m);
for (int i = 1; i <= m; i++) {
if (s[i].empty()) continue;
auto it = s[i].begin();
sgt1.upd(1, 1, m, i, *it);
}
for (int i = 1; i <= m; i++) bit.add(i, b[i]);
int lst = m + 1;
for (int i = m; i >= 1; i--) {
if (s[i].empty()) continue;
auto it = s[i].begin();
int nr = it -> fi;
if (nr < lst) {
// cout << "[" << i << ", " << nr << "]\n";
curL.insert(make_pair(i, it -> se));
curR.insert(make_pair(nr, it -> se));
int val = bit.qry(i, nr);
sgt2.upd(1, 1, m, i, make_pair(val, it -> se));
s[i].erase(it);
if (!s[i].empty()) sgt1.upd(1, 1, m, i, *s[i].begin());
else sgt1.upd(1, 1, m, i, make_pair(inf, 0));
lst = nr;
}
}
vector <int> ans;
for (int i = 1; i <= n; i++) {
int u = sgt2.id[1];
ans.emplace_back(u);
sgt2.upd(1, 1, m, l[u], make_pair(inf, 0));
auto it = curL.find(make_pair(l[u], u));
curL.erase(it);
it = curR.find(make_pair(r[u], u));
curR.erase(it);
auto L = rest.lower_bound(l[u]);
auto R = L;
for (auto it = L; *it <= r[u]; it++, R = it) {
if (it == rest.end()) break;
int p = *it;
bit.add(p, -b[p]);
auto itL = curR.lower_bound(make_pair(p, 0));
int nl = itL -> se;
nl = l[nl];
auto itR = curL.upper_bound(make_pair(p, n + 1));
itR--;
int nr = itR -> fi;
sgt2.mdf(1, 1, m, nl, nr, -b[p]);
}
rest.erase(L, R);
auto itR = curL.upper_bound(make_pair(l[u], 0));
auto itL = itR;
itL--;
int l1 = itL -> fi, r1 = r[itL -> se];
int l2 = itR -> fi, r2 = r[itR -> se];
int lst = l1;
while (true) {
int id = sgt1.qry(1, 1, m, lst + 1, l2 - 1).se;
if (!id) break;
int nl = l[id], nr = r[id];
if (nr >= r2) break;
int val = bit.qry(nl, nr);
sgt2.upd(1, 1, m, nl, make_pair(val, id));
curL.insert(make_pair(nl, id));
curR.insert(make_pair(nr, id));
s[nl].erase(make_pair(nr, id));
if (!s[nl].empty()) sgt1.upd(1, 1, m, nl, *s[nl].begin());
else sgt1.upd(1, 1, m, nl, make_pair(inf, 0));
lst = nl;
}
}
for (auto x : ans) cout << x << " ";
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << "ms\n";
return 0;
}
/*
4
3 7
10 14
1 6
6 11
*/
K. Fake Plastic Trees 2
给定一棵
给定非负整数
考虑一个暴力 DP:设
注意到
想要进一步优化则需要发现一些更好的性质。
引理
:如果 ,且 ,且 ,那么将 置为 也不会改变最终答案。
证明:设
由引理
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
constexpr int N = 1e3 + 5, K = 55;
bool Mbe;
int n, k; LL L, R, a[N];
vector <int> e[N];
vector <LL> f[N][K], g[K];
int sz[N];
bool chk(LL x) {
return L <= x && x <= R;
}
void dfs(int u, int ff) {
sz[u] = 1;
for (int i = 0; i <= k; i++) f[u][i].clear();
f[u][0].emplace_back(a[u]);
for (auto v : e[u]) {
if (v == ff) continue;
dfs(v, u);
for (int i = 0; i <= k; i++) g[i].clear();
for (int i = 0; i <= min(sz[u] - 1, k); i++)
for (int j = 0; j <= min(sz[v] - 1, k - i); j++)
for (LL p : f[u][i]) {
bool ok = false;
for (LL q : f[v][j]) {
if (p + q <= R) g[i + j].emplace_back(p + q);
if (chk(q)) ok = 1;
}
if (ok && i + j + 1 <= k) g[i + j + 1].emplace_back(p);
}
sz[u] += sz[v];
for (int i = 0; i <= min(sz[u] - 1, k); i++) {
sort(g[i].begin(), g[i].end());
f[u][i].clear();
int c = 0;
for (LL v : g[i]) {
while (c > 1 && v - f[u][i][c - 2] < R - L) c--, f[u][i].pop_back();
f[u][i].emplace_back(v);
c++;
}
}
}
}
void solve() {
cin >> n >> k >> L >> R;
for (int i = 1; i <= n; i++) cin >> a[i], e[i].clear();
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
dfs(1, 0);
for (int i = 0; i <= k; i++) {
bool ok = false;
for (LL j : f[1][i]) ok |= chk(j);
if (ok) cout << "1";
else cout << "0";
}
cout << "\n";
}
bool Med;
int main() {
// fprintf(stderr, "%.9lf\n", 1.0 * (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t; cin >> t;
while (t--) solve();
// cerr << 1e3 * clock() / CLOCKS_PER_SEC << "ms\n";
return 0;
}
/*
3
4 3 1 2
1 1 1 1
1 2
2 3
3 4
4 3 1 2
1 1 1 1
1 2
1 3
1 4
4 3 0 0
1 1 1 1
1 2
1 3
1 4
*/
L. Curly Racetrack
你是一个水管工。有一个
注意上述水管可以旋转。我们称第四种蓝色高亮标识的水管为高级的。如果一个局面中每个位置都放置了如上六种水管之一,并且所有水管都连起来了(每条水管接口都与相邻的水管相连),那么就称这个局面是合法的。注意不能朝向边界外。
目前有一些格子中放置了高级水管,你能在若干个其他格子中放置一些高级水管。在此之后,另一个水管工会尝试在剩余未被放置水管的格子中放置水管,使得最终变成一个合法的局面。注意你和你的同伴不能旋转那些提前放置的水管。此外,对某些格子,你不能够在其中放入高级水管,并且对另外某些格子,你必须要在其中放入一种高级水管。
你需要求出在你放完高级水管之后最多有多少个格子中为高级水管,并使得你的同伴能完成工作。无解输出
给每条边赋一个状态,表示有没有水管通过这条边相连。考虑一个高级水管即为要求一个格子中相对的两条边状态不同。题目即为给定一些边的状态,给定一些格子能否或者必须为好格子,求最多有几个格子为好格子。
注意到行和列几乎独立,对于一行竖着的边,如果相邻两个有要求的边奇偶性不对,即不能满足中间的格子均为好格子,则说明中间的格子中至少一个为坏格子。对于列类似。
进一步发现,我们仅有形如这样的限制。即,如果我们选择一些格子不一定为好格子,使得上述要求(即一行或一列中某些格子至少一个被选择)均被满足,那么一定能够构造出一种方案使得其他格子均为好格子。
这样就可以建立一个二分图,左侧点为所有行限制,右侧点为所有列限制,对于两个相交的限制,如果交点所在格子可以被选择为坏格子,那么在两个限制间连一条边。问题变成选择最少的边,使得每个点周围的边中都至少一个被选择了。即为最小边覆盖,跑
一年前在模拟赛碰到过这题,所以代码是一年前的。
code
/*
最黯淡的一个 梦最为炽热
万千孤单焰火 让这虚构灵魂鲜活
至少在这一刻 热爱不问为何
存在为将心声响彻
*/
#include <bits/stdc++.h>
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define int long long
#define mem(x, v) memset(x, v, sizeof(x))
#define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n))
#define lob lower_bound
#define upb upper_bound
using namespace std;
inline int read() {
int x = 0, w = 1;char ch = getchar();
while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); }
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x * w;
}
const int MN = 1e2 + 5;
const int Mod = 998244353;
const int inf = 1e9;
inline int qPow(int a, int b = Mod - 2, int ret = 1) {
while (b) {
if (b & 1) ret = ret * a % Mod;
a = a * a % Mod, b >>= 1;
}
return ret;
}
// #define dbg
int N, M, cnt, S, T, c[MN], idL[MN][MN], idR[MN][MN];
char a[MN][MN];
vector <int> L, R;
namespace Dinic {
const int MN = 2e4 + 5, MM = 1e5 + 5;
int N, S, T, mxf;
int fr[MN], cur[MN], v[MN], f[MN], nxt[MN], tot;
int q[MN], hd, tl, d[MN];
inline void add(int x, int y, int z) {
v[++tot] = y, f[tot] = z, nxt[tot] = fr[x], fr[x] = tot;
v[++tot] = x, f[tot] = 0, nxt[tot] = fr[y], fr[y] = tot;
}
inline bool BFS() {
for (int i = 1; i <= N; i++) d[i] = 0;
hd = tl = 0, q[tl++] = S, d[S] = 1;
while (hd != tl) {
int x = q[hd++];
for (int i = fr[x]; i; i = nxt[i]) {
int y = v[i], z = f[i];
if (d[y] || !z) continue;
d[y] = d[x] + 1, q[tl++] = y;
if (y == T) return true;
}
}
return false;
}
inline int DFS(int x, int now) {
if (x == T) return now;
int rest = now;
for (int &i = cur[x]; i; i = nxt[i]) {
int y = v[i], z = f[i];
if (d[y] != d[x] + 1 || !z) continue;
int k = DFS(y, min(z, rest));
if (!k) d[y] = 0;
else f[i] -= k, f[i ^ 1] += k, rest -= k;
if (!rest) break;
}
return now - rest;
}
inline void work() {
while (BFS()) {
for (int i = 1; i <= N; i++) cur[i] = fr[i];
int cur;
while (cur = DFS(S, inf)) mxf += cur;
}
}
inline void init(int _N, int _S, int _T) {
N = _N, S = _S, T = _T, mxf = 0, tot = 1;
for (int i = 1; i <= N; i++) fr[i] = 0;
}
}
signed main(void) {
N = read(), M = read();
for (int i = 1; i <= N; i++) scanf("%s", a[i] + 1);
int fl = 1;
auto chk = [&](int x, int k) {
if (c[x] == -1) c[x] = k;
else if (c[x] != k) fl = 0;
};
for (int i = 1; i <= N; i++) {
mem(c, -1);
c[0] = c[M] = 0;
for (int j = 1; j <= M; j++) {
if (a[i][j] == '1' || a[i][j] == '2') {
chk(j - 1, 1), chk(j, 0);
}
if (a[i][j] == '3' || a[i][j] == '4') {
chk(j - 1, 0), chk(j, 1);
}
}
for (int j = 0, k = 1; j < M; j = k++) {
while (!~c[k]) k++;
if (((j - k) & 1) == (c[j] ^ c[k])) continue;
int o = 0;
for (int p = j + 1; p <= k; p++) if (a[i][p] == 'x') o = 1;
if (o) continue;
++cnt, L.pb(cnt);
for (int p = j + 1; p <= k; p++) {
idL[i][p] = cnt;
if (a[i][p] == '.') o = 1;
}
if (!o) fl = 0;
}
}
for (int i = 1; i <= M; i++) {
mem(c, -1);
c[0] = c[N] = 0;
for (int j = 1; j <= N; j++) {
if (a[j][i] == '1' || a[j][i] == '3') {
chk(j - 1, 1), chk(j, 0);
}
if (a[j][i] == '2' || a[j][i] == '4') {
chk(j - 1, 0), chk(j, 1);
}
}
for (int j = 0, k = 1; j < N; j = k++) {
while (!~c[k]) k++;
if (((j - k) & 1) == (c[j] ^ c[k])) continue;
int o = 0;
for (int p = j + 1; p <= k; p++) if (a[p][i] == 'x') o = 1;
if (o) continue;
++cnt, R.pb(cnt);
for (int p = j + 1; p <= k; p++) {
idR[p][i] = cnt;
if (a[p][i] == '.') o = 1;
}
if (!o) fl = 0;
}
}
if (!fl) return puts("-1"), 0;
S = cnt + 1, T = S + 1, Dinic :: init(T, S, T);
for (int o : L) Dinic :: add(S, o, 1);
for (int o : R) Dinic :: add(o, T, 1);
int ans = N * M;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= M; j++) {
if (a[i][j] == 'x') ans--;
else if (a[i][j] == '.' && idL[i][j] && idR[i][j]) Dinic :: add(idL[i][j], idR[i][j], 1);
}
}
Dinic :: work(), ans -= cnt - Dinic :: mxf;
printf("%lld\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具