洛谷P16314(来自知识的礼物)做题笔记
思路
观察题目,记某一行有翻转 / 不翻转两种状态,则我们发现如果 $ a_{i, j} = 1 $,且 $ a_{k, j} = 1 $,其中 $ k < i $,则第 \(i\) 行与第 \(k\) 行的状态一定不同。
同理,若 $ a_{i, j} = 1 $ 且 $ a_{k, m - j + 1} = 1 $,则第 \(i\) 行与第 \(k\) 行状态一定相同。
如果信息有冲突,则无解,输出 \(0\)。
行间状态仅存在相同 / 不同,想到并查集。
记录一个 vector $ b_{i, j} $,若 $ a_{i, j} = 1 $,则 $ b_{i, j} $ 记录所有上文的 \(k\)。
我们在读取第 \(i\) 行时,若 $ a_{i, j} = 1 $,那么遍历 $ b_{i, j} $,将其中所有行与 \(i\) 标记为不同;遍历 $ b_{i, m - j + 1} $,其中所有行与 \(i\) 相同。
容易发现 $ b $ 可以省掉第一维。
// 此处定义了快读快写 io
const int N = 1e6 + 7, MOD = 1e9 + 7;
char t[N];
int fa[N << 1], n, m;
vector<int> b[N];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int x, int y) {
if ((x = find(x)) == (y = find(y))) return;
if (x > y) swap(x, y);
fa[y] = x;
}
bool check(int x, int y) { return find(x) == find(y); }
bool markln(int x, int y, bool d) { // d=0: same d=1: different
if (check(x, y + n * (d ^ 1))) return 0;
merge(x, y + n * d), merge(x + n, y + n * (d ^ 1));
return 1;
}
bool upd(int u, int j, bool d) {
for (int v : b[j]) {
if (v == u) return 1;
if (!markln(u, v, d)) return 0;
}
if (d) b[j].push_back(u);
return 1;
}
ll qpow(ll a, ll b, ll p) {
ll res = 1;
while (b) {
if (b & 1) (res *= a) %= p;
(a *= a) %= p, b >>= 1;
}
return res;
}
void solve() {
io.readint(n), io.readint(m);
for (int i = 1; i <= m; ++i) b[i].clear();
for (int i = 1; i <= n; ++i) {
fa[i] = i, fa[i + n] = i + n;
io.readstr(t + 1);
for (int j = 1; j <= m; ++j) {
bool u = t[j] ^ 48;
if (!u) continue;
if (!upd(i, j, 1) || !upd(i, m - j + 1, 0)) {
io.writechar('0');
return;
}
}
}
ll cnt = 0;
for (int i = 1; i <= n + n; ++i) {
if (fa[i] == i) ++cnt;
}
io.write(qpow(2, cnt >> 1, MOD));
}
赛场上没有发现任何问题……
事后分析,代码在发现冲突时,直接结束了 solve 函数,导致多组数据后面的信息没有读完。
改进之后:
void solve() {
...
bool flag = false;
for (int i = 1; i <= n; ++i) {
io.readstr(t + 1);
if (flag) continue;
for (int j = 1; j <= m; ++j) {
...
if (...) {
io.writechar('0');
flag = true;
break;
}
}
}
if (flag) return;
...
}
时间复杂度为 $ O(nm) $,可过。

浙公网安备 33010602011771号