MX galaxy Day5
P6378 [PA 2010] Riddle
\(2-SAT\) 。
对每个部分内进行前后缀优化建图,每条边的端点或起来为 \(1\) 。
这样可能会出现每个部分内一个点都没选,但我们发现这样没关系。
因为任意选择一个点都是可行的,不会影响其他部分,也不会让边变得不合法。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 5e6 + 7;
int n, m, k, w;
int dfn[_], low[_], idx, scc, bel[_], stk[_], top; bool in[_];
std::vector <int> g[_], p;
void Tarjan(int u) {
dfn[u] = low[u] = ++idx, stk[++top] = u, in[u] = true;
for (int v : g[u]) {
if (!dfn[v]) Tarjan(v), low[u] = std::min(low[u], low[v]);
else if (in[v]) low[u] = std::min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) { ++scc; int nw;
do {
nw = stk[top--], bel[nw] = scc, in[nw] = false;
} while (nw != u);
}
}
int id(int x, int i) { return x + i * n; } //0 不选 1 选 2 前缀不选 3 后缀不选
int main() {
scanf("%d%d%d", & n, & m, & k); int a, b;
lep(i, 1, m) scanf("%d%d", & a, & b),
g[id(a, 0)].push_back(id(b, 1)),
g[id(b, 0)].push_back(id(a, 1));
lep(i, 1, k) {
scanf("%d", & w);
while (w--) scanf("%d", & a), p.push_back(a);
for (int k = 0; k < (int)p.size(); ++k) {
g[id(p[k], 2)].push_back(id(p[k], 0)),
g[id(p[k], 3)].push_back(id(p[k], 0));
if (k)
g[id(p[k], 2)].push_back(id(p[k - 1], 2)),
g[id(p[k], 1)].push_back(id(p[k - 1], 2));
if (k != p.size() - 1)
g[id(p[k], 3)].push_back(id(p[k + 1], 3)),
g[id(p[k], 1)].push_back(id(p[k + 1], 3));
}
p.clear();
}
lep(i, 1, 4 * n) if (!dfn[i]) Tarjan(i);
lep(i, 1, n) if (bel[i] == bel[i + n]) { puts("NIE"); return 0; }
puts("TAK");
return 0;
}
P7477 「C.E.L.U-02」划分可重集
容易发现,每个元素只会属于一个集合,因为多属于一个意味着更多的限制。
看到题目肯定容易想到二分。
然后我们考虑如何将限制显式地连出来,发现满足存在限制的点对形式如同二维偏序,所以我们用 \(CDQ\) 来优化建图。
具体地,以 \(A\) 集合为例,对于 \(CDQ\) 分治的每一层,发现连边形如,左侧前缀向右侧单点连,右侧单点向左侧前缀连。
再加上前缀优化建图即可。
\(B\) 集合同理,不过变成后缀优化建图。
不要用主席树,空间会倒闭。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 1e6 + 7;
const int inf = 1e9;
typedef long long ll;
enum { A, B }; int bel[_]; bool vis[_];
int n, m, w[_], x[_], y[_], ud[_], idx, scc, d[_], pi[_], po[_], ni[_], no[_];
std::vector <int> e[_], g[_];
void Add(int u, int v) { e[u].push_back(v), g[v].push_back(u); }
int id(int x, int op) { return x + op * n; }
void cdq(int l, int r, int k) {
if (l == r) return; int md = (l + r) >> 1;
cdq(l, md, k), cdq(md + 1, r, k);
auto cmp = [](const int& x, const int& y) { return w[x] < w[y]; };
std::sort(ud + l, ud + md + 1, cmp), std::sort(ud + md + 1, ud + r + 1, cmp);
lep(i, l, md) {
pi[i] = ++idx, po[i] = ++idx;
if (i != l) Add(pi[i], pi[i - 1]), Add(po[i - 1], po[i]);
Add(pi[i], id(ud[i], B)), Add(id(ud[i], A), po[i]);
}
rep(i, md, l) {
ni[i] = ++idx, no[i] = ++idx;
if (i != md) Add(ni[i], ni[i + 1]), Add(no[i + 1], no[i]);
Add(ni[i], id(ud[i], A)), Add(id(ud[i], B), no[i]);
}
int R = l - 1;
lep(i, md + 1, r) {
while (R < md and w[ud[R + 1]] <= w[ud[i]] - k) ++R;
if (R >= l and w[ud[R]] <= w[ud[i]] - k) Add(id(ud[i], A), pi[R]), Add(po[R], id(ud[i], B));
}
R = md + 1;
rep(i, r, md + 1) {
while (R > l and w[ud[R - 1]] >= w[ud[i]] + k) --R;
if (R <= md and w[ud[R]] >= w[ud[i]] + k) Add(id(ud[i], B), ni[R]), Add(no[R], id(ud[i], A));
}
}
void lnk(int k) {
lep(i, 1, 2 * n) ud[i] = i; idx = 2 * n;
cdq(1, n, k);
lep(i, 1, m) Add(id(x[i], A), id(y[i], B)), Add(id(x[i], B), id(y[i], A)),
Add(id(y[i], A), id(x[i], B)), Add(id(y[i], B), id(x[i], A));
}
void dfs1(int u) { vis[u] = true; for (int v : e[u]) if (!vis[v]) dfs1(v); d[++*d] = u; }
void dfs2(int u) { bel[u] = scc; for (int v : g[u]) if (!bel[v]) dfs2(v); }
void cls() { lep(i, 1, idx) vis[i] = bel[i] = 0, e[i].clear(), g[i].clear(); }
bool Check(int k) {
lnk(k);
lep(i, 1, idx) if (!vis[i]) dfs1(i);
rep(i, idx, 1) if (!bel[d[i]]) ++scc, dfs2(d[i]);
*d = scc = 0;
lep(i, 1, n) if (bel[id(i, A)] == bel[id(i, B)]) { cls(); return false; }
lep(i, 1, m) if (bel[id(x[i], A)] == bel[id(y[i], A)] or bel[id(x[i], B)] == bel[id(y[i], B)]) { cls(); return false; }
cls(); return true;
}
int main() {
scanf("%d%d", & n, & m);
lep(i, 1, n) scanf("%d", w + i);
lep(i, 1, m) scanf("%d%d", x + i, y + i);
int l = 0, r = inf;
while (l < r) {
int mid = (l + r) >> 1;
if (Check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", r == inf ? -1 : r);
return 0;
}
P6965 [NEERC 2016] Binary Code
看到前缀相关,考虑建 \(01\) \(tire\) 。
然后对于每个含有 \(?\) 的串,填 \(0\) 和 填 \(1\) 作为两个文字,生成的两个串插入 \(01\) \(trie\) 中。
然后每个限制形如,一个点选了,那么和它位置相同的或者位置具有祖先后代关系的不能选。
发现 \(tire\) 本身就具有良好的结构,树上前缀和优化建图,对于每个节点内部再加上前后缀优化建图即可。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 5e6 + 7;
typedef long long ll;
int n, len[_], nw, pos[_]; char s[_];
int ch[_][2], idx = 1, tot;
std::vector <int> Ed[_]; int pi[_], ni[_], oi[_], po[_], dl[_], dr[_];
std::vector <int> e[_], g[_]; int scc, bel[_], d[_]; bool vis[_];
void Add(int u, int v) { e[u].push_back(v), g[v].push_back(u); }
void dfs1(int u) { vis[u] = true; for (int v : e[u]) if (!vis[v]) dfs1(v); d[++*d] = u; }
void dfs2(int u) { bel[u] = scc; for (int v : g[u]) if (!bel[v]) dfs2(v); }
void Ins(char s[], int id) {
int nw = 1, len = std::strlen(s) - 1;
lep(i, 0, len) { int k = s[i] - '0';
if (!ch[nw][k]) ch[nw][k] = ++idx;
nw = ch[nw][k];
}
Ed[nw].push_back(id);
}
void Lnk(int u, int lst) {
if (Ed[u].size()) {
for (int i = 0; i < (int)Ed[u].size(); ++i) {
dl[i] = ++tot, dr[i] = ++tot, po[i] = ++tot;
if (i) Add(dl[i], dl[i - 1]), Add(dr[i - 1], dr[i]), Add(po[i], po[i - 1]);
Add(dl[i], Ed[u][i] ^ 1), Add(dr[i], Ed[u][i] ^ 1), Add(Ed[u][i], po[i]);
if (i) Add(Ed[u][i], dl[i - 1]), Add(Ed[u][i - 1], dr[i]);
}
pi[u] = ++tot, oi[u] = ++tot, ni[u] = po[0];
Add(pi[u], dr[0]), Add(oi[u], dr[0]);
if (lst) Add(ni[u], pi[lst]), Add(pi[u], pi[lst]), Add(ni[lst], oi[u]), Add(oi[lst], oi[u]);
lst = u;
}
if (ch[u][0]) Lnk(ch[u][0], lst);
if (ch[u][1]) Lnk(ch[u][1], lst);
}
void Solve() {
*d = 1;
lep(i, 2, tot) if (!vis[i]) dfs1(i);
rep(i, tot, 2) if (!bel[d[i]]) ++scc, dfs2(d[i]);
}
int main() {
scanf("%d", & n);
nw = 1;
lep(i, 1, n) {
scanf("%s", s + nw); len[i] = std::strlen(s + nw);
lep(j, nw, nw + len[i] - 1) if (s[j] == '?') { pos[i] = j; break; }
if (pos[i]) s[pos[i]] = '0', Ins(s + nw, i << 1), s[pos[i]] = '1', Ins(s + nw, i << 1 | 1);
else Ins(s + nw, i << 1), Ins(s + nw, i << 1 | 1);
nw += len[i] + 1;
} tot = n << 1 | 1;
Lnk(1, 0), Solve();
lep(i, 1, n) if (bel[i << 1 | 1] == bel[i << 1]) { puts("NO"); return 0; }
puts("YES");
nw = 1;
lep(i, 1, n) {
if (pos[i] and bel[i << 1] > bel[i << 1 | 1]) s[pos[i]] = '0';
else if (pos[i]) s[pos[i]] = '1';
printf("%s\n", s + nw); nw += len[i] + 1;
}
return 0;
}
P9139 [THUPC 2023 初赛] 喵了个喵 II
先考虑如果每个元素恰好出现 \(2\) 次怎么分,发现将 \(i\) 两次出现的位置记作 \((l_i, r_i)\) ,那么有解的充要条件是:不存在两个区间相互包含。
必要性显然,充分性直接将所有的 \(l_i\) 作为一个子序列,所有的 \(r_i\) 作为另一个就好了。
但是现在每个元素出现 \(4\) 次,我们考虑修改一些这个做法。
可以枚举这 \(4\) 个位置的划分顺序,将他们看作两种不同的颜色,发现只有三种划分情况:
- \(\color{red} 1\) \(\color{red} 2\) \(\color{green} 3\) \(\color{green} 4\)
- \(\color{red} 1\) \(\color{green} 2\) \(\color{red} 3\) \(\color{green} 4\)
- \(\color{red} 1\) \(\color{green} 2\) \(\color{green} 3\) \(\color{red} 4\)
且第三种情况以及出现了包含,所以只有两种方案,转化为 \(2-SAT\) 问题。
对于限制,发现依旧是二维偏序的形式,\(CDQ\) 优化建图即可。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 5e6 + 7;
typedef long long ll;
struct node { int l, r, id; }s[_]; int len;
int n, a[_], pos[_][4], idx, pi[_], po[_];
std::vector <int> e[_], g[_]; int bel[_], scc, d[_]; bool vis[_], ans[_];
void Add(int u, int v) { e[u].push_back(v), g[v].push_back(u); }
void CDQ(int l, int r) {
if (l == r) return; int md = (l + r) >> 1;
CDQ(l, md), CDQ(md + 1, r);
auto cmp = [](const node&x, const node&y) { return x.l > y.l; };
std::sort(s + l, s + md + 1, cmp), std::sort(s + md + 1, s + r + 1, cmp);
lep(i, l, md) {
pi[i] = ++idx, po[i] = ++idx;
if (i != l) Add(pi[i], pi[i - 1]), Add(po[i - 1], po[i]);
Add(pi[i], s[i].id ^ 1), Add(s[i].id, po[i]);
}
int R = l - 1;
lep(i, md + 1, r) {
while (R < md and s[R + 1].l >= s[i].l) ++R;
if (R >= l and s[R].l >= s[i].l) Add(s[i].id, pi[R]), Add(po[R], s[i].id ^ 1);
}
}
void Dfs1(int u) { vis[u] = true; for (int v : e[u]) if (!vis[v]) Dfs1(v); d[++*d] = u; }
void Dfs2(int u) { bel[u] = scc; for (int v : g[u]) if (!bel[v]) Dfs2(v); }
int main() {
scanf("%d", & n);
lep(i, 1, n * 4) {
scanf("%d", a + i);
lep(j, 0, 3) if (!pos[a[i]][j]) { pos[a[i]][j] = i; break; }
}
lep(i, 1, n) s[++len] = { pos[i][0], pos[i][2], i << 1 | 1 }, s[++len] = { pos[i][1], pos[i][3], i << 1 | 1 },
s[++len] = { pos[i][0], pos[i][1], i << 1 }, s[++len] = { pos[i][2], pos[i][3], i << 1 };
idx = n << 1 | 1;
std::sort(s + 1, s + 1 + len, [](const node&x, const node&y) { return x.r != y.r ? x.r < y.r : x.l > y.l; });
CDQ(1, len);
*d = 1;
lep(i, 2, idx) if (!vis[i]) Dfs1(i);
rep(i, idx, 2) if (!bel[d[i]]) ++scc, Dfs2(d[i]);
lep(i, 1, n) if (bel[i << 1 | 1] == bel[i << 1]) { puts("No"); return 0; }
puts("Yes");
lep(i, 1, n) {
if (bel[i << 1 | 1] > bel[i << 1]) ans[pos[i][2]] = ans[pos[i][3]] = true;
else ans[pos[i][1]] = ans[pos[i][3]] = true;
}
lep(i, 1, n * 4) putchar('0' + ans[i]);
puts("");
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下