题解:P9139 [THUPC 2023 初赛] 喵了个喵 II
Blog
这或许是第一道我独立想出的黑题。
对每个元素观察匹配上的位置。假设对于元素
注意,此处的
进一步考虑两种元素之间,什么样的匹配是不合法的。显然如果匹配间的连线是相互嵌套的关系,则一定是不合法的。形式化地,如果
由于每个元素的状态可以用一个
注意到这个建图的形式,是向一个区间的子区间进行连边,而子区间的条件刚好又可以放在二维平面上进行刻画,于是问题变为了二维偏序类的连边。可以直接使用主席树优化建图 + Tarjan 求强连通分量,得到一组合法的 2-SAT 解。时间复杂度
但是主席树的时间空间常数都太大了,于是我们考虑使用 Kosaraju 算法求强连通分量。这个算法的好处在于,它并不需要显式地进行建图,而是只需要支持“寻找出边中所有未被访问到的节点”即可,这里可以直接使用朴素线段树进行维护。时间复杂度
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc (p << 1)
#define rc ((p << 1) | 1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef long double ldb;
typedef __int128 i128;
using pi = pair<int, int>;
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int n, a[N], scc[N], scctot;
vector<int> pos[N], topo;
set<pi> Rpos[N];
pi ranges[N][2];
map<pi, int> MappL;
struct Node{
int l, r;
pi mx, mn;
};
struct Segtree{
Node tr[4 * N];
void pushup(Node &p, Node ls, Node rs) {
p.mx = max(ls.mx, rs.mx);
p.mn = min(ls.mn, rs.mn);
}
void build(int p, int ln, int rn) {
tr[p] = {ln, rn, {-inf, -inf}, {inf, inf}};
if(ln == rn) {
Rpos[ln].clear();
return;
}
int mid = (ln + rn) >> 1;
build(lc, ln, mid);
build(rc, mid + 1, rn);
pushup(tr[p], tr[lc], tr[rc]);
}
void update(int p, int pos, pi rg, bool typ) {
if(tr[p].l == tr[p].r) {
if(typ == 0) Rpos[pos].erase(rg);
else Rpos[pos].insert(rg);
if(Rpos[pos].empty()) {
tr[p].mn = {inf, inf};
tr[p].mx = {-inf, -inf};
}
else {
tr[p].mn = *Rpos[pos].begin();
tr[p].mx = *Rpos[pos].rbegin();
}
return;
}
int mid = (tr[p].l + tr[p].r) >> 1;
if(pos <= mid) update(lc, pos, rg, typ);
else update(rc, pos, rg, typ);
pushup(tr[p], tr[lc], tr[rc]);
}
Node query(int p, int ln, int rn) {
if(ln <= tr[p].l && tr[p].r <= rn) {
return tr[p];
}
int mid = (tr[p].l + tr[p].r) >> 1;
if(rn <= mid) return query(lc, ln, rn);
if(ln >= mid + 1) return query(rc, ln, rn);
Node res;
pushup(res, query(lc, ln, rn), query(rc, ln, rn));
return res;
}
} tr1;
bitset<N> vis, ans;
void Kosaraju(int u, int typ) {
vis[u] = 1;
for(int i = 0; i <= 1; i++) {
int l = ranges[u][i].fi, r = ranges[u][i].se;
Node res = tr1.query(1, l + 1, r);
while(res.mn.fi <= r) {
int id = res.mn.se;
int ql = MappL[res.mn];
tr1.update(1, ql, res.mn, 0);
if(!vis[id]) {
Kosaraju(id, typ);
}
res = tr1.query(1, l + 1, r);
}
res = tr1.query(1, 1, l);
while(res.mx.fi > r) {
int id = res.mx.se;
int ql = MappL[res.mx];
tr1.update(1, ql, res.mx, 0);
if(!vis[id]) {
Kosaraju(id, typ);
}
res = tr1.query(1, 1, l);
}
}
if(typ == 0) {
topo.push_back(u);
}
else {
scc[u] = scctot;
}
}
int main()
{
// freopen("P9139.in", "r", stdin);
// freopen("P9139.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= 4 * n; i++) {
cin >> a[i];
pos[a[i]].push_back(i);
}
// DFS 1
tr1.build(1, 1, 4 * n);
for(int i = 1; i <= n; i++) {
int p1 = pos[i][0], p2 = pos[i][1], p3 = pos[i][2], p4 = pos[i][3];
tr1.update(1, p1, {p3, i + n}, 1);
tr1.update(1, p2, {p4, i + n}, 1);
ranges[i][0] = {p1, p3};
ranges[i][1] = {p2, p4};
MappL[{p3, i + n}] = p1;
MappL[{p4, i + n}] = p2;
tr1.update(1, p1, {p2, i}, 1);
tr1.update(1, p3, {p4, i}, 1);
ranges[i + n][0] = {p1, p2};
ranges[i + n][1] = {p3, p4};
MappL[{p2, i}] = p1;
MappL[{p4, i}] = p3;
}
for(int i = 1; i <= 2 * n; i++) {
if(!vis[i]) {
Kosaraju(i, 0);
}
}
// DFS2
vis.reset();
tr1.build(1, 1, 4 * n);
for(int i = 1; i <= n; i++) {
int p1 = pos[i][0], p2 = pos[i][1], p3 = pos[i][2], p4 = pos[i][3];
tr1.update(1, p1, {p3, i}, 1);
tr1.update(1, p2, {p4, i}, 1);
ranges[i][0] = {p1, p2};
ranges[i][1] = {p3, p4};
MappL[{p3, i}] = p1;
MappL[{p4, i}] = p2;
tr1.update(1, p1, {p2, i + n}, 1);
tr1.update(1, p3, {p4, i + n}, 1);
ranges[i + n][0] = {p1, p3};
ranges[i + n][1] = {p2, p4};
MappL[{p2, i + n}] = p1;
MappL[{p4, i + n}] = p3;
}
for(int i = topo.size() - 1; i >= 0; i--) {
int u = topo[i];
if(!vis[u]) {
++scctot;
Kosaraju(u, 1);
}
}
for(int i = 1; i <= n; i++) {
if(scc[i] == scc[i + n]) {
cout << "No\n";
return 0;
}
int p1 = pos[i][0], p2 = pos[i][1], p3 = pos[i][2], p4 = pos[i][3];
if(scc[i] > scc[i + n]) {
ans[p1] = ans[p2] = 1;
}
else {
ans[p1] = ans[p3] = 1;
}
}
cout << "Yes\n";
for(int i = 1; i <= 4 * n; i++) {
cout << ans[i];
}
return 0;
}