题解:ARC232A Two in the Range
挺有趣味的。
赛时做法,貌似和官解有点区别。
如果把所有 \(1\) 交替染成两种颜色,每个区间恰好包含两个异色的 \(1\)。于是问题可以转化为,找出两组不相交的位置,使得每个区间在每组中恰好包含一个位置。
先考虑一组位置的限制。考察相邻的两个位置 \(x<y\),不难发现:
- 若 \(L_i\leq x\) 则 \(y>R_i\)。
- 若 \(L_i>x\) 则 \(y\leq R_i\)。
因此若固定 \(x\),则合法的后继位置 \(y\) 满足 \(l_x\leq y\leq r_x\),其中 \(l_x=\max\left(x,\max\limits_{L_i\leq x}R_i\right)+1\),\(r_x=\min\limits_{L_i>x}R_i\)。
首尾位置 \(p,q\) 需要满足 \(p\leq\min R_i\) 且 \(q\geq\max L_i\)。建出有向图:
- 源点 \(S\) 向每个 \(\leq\min R_i\) 的点连边。
- 每个点 \(x\) 向 \([l_x,r_x]\) 内的点连边。
- 每个 \(\geq\max L_i\) 的点向汇点 \(T\) 连边。
那么问题转化为,判断能否在这个有向图上找到两条不交的 \(S\) 到 \(T\) 的路径。
这是经典问题。考虑网络流,把每个点 \(x\) 拆成 \(in_x\) 和 \(out_x\),\(in_x\) 向 \(out_x\) 连容量为 \(1\) 的边,其他边容量设为 \(2\),判断最大流是否 \(=2\) 即可。那么我们实际上可以手动模拟两轮增广,但是问题在于边数是 \(\mathcal{O}(n^2)\) 的。
先考虑第一轮增广。每个出点连接的都是一个区间内的入点,可以直接用并查集维护所有尚未入队的入点,支持查询第一个 \(\geq x\) 的未入队点和删除某个点,不断取出区间内尚未入队的入点即可。
第一轮增广成功后可以得到一条路径。对于第二轮增广:
- 只有不在路径上的入点可以访问对应出点。
- 在路径上的出入点可以沿着唯一一条反向边往回走。
- 每个出点仍然可以到达区间内的入点。
类似维护即可。
时间复杂度为 \(\mathcal{O}(m+n\log{n})\) 或者 \(\mathcal{O}(m+n\alpha(n))\)。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 5e5 + 5;
template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
constexpr int lg2(ll x) { return 63 ^ __builtin_clzll(x); }
constexpr ll bitCeil(ll x) { return x == 1 ? 1ll : 1ll << lg2(x - 1) + 1; }
int tc, n, m, mx, mn, pre[MAXN], suf[MAXN], prv[MAXN << 1], fr[MAXN << 1];
bool vis[MAXN << 1], mark[MAXN];
struct DSU {
int fa[MAXN];
void init() {
iota(fa + 1, fa + n + 2, 1);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void unite(int x, int y) {
fa[find(x)] = find(y);
}
} dsu;
int bfs() {
fill(vis + 1, vis + n * 2 + 1, false);
dsu.init();
queue<int> Q;
for (int i = 1; i <= mn; ++i) {
Q.emplace(i);
dsu.unite(i, i + 1);
vis[i] = true;
}
while (!Q.empty()) {
int x = Q.front();
Q.pop();
if (x <= n) {
if (!mark[x] && !vis[x + n]) {
Q.emplace(x + n);
vis[x + n] = true;
fr[x + n] = x;
}
if (prv[x] && !vis[prv[x]]) {
Q.emplace(prv[x]);
vis[prv[x]] = true;
}
} else {
if (x - n >= mx) return x;
if (prv[x] && !vis[prv[x]]) {
Q.emplace(prv[x]);
vis[prv[x]] = true;
dsu.unite(prv[x], prv[x] + 1);
}
int L = max(x - n, pre[x - n]) + 1, R = suf[x - n + 1], p = dsu.find(L);
while (p <= R) {
Q.emplace(p);
vis[p] = true;
fr[p] = x;
dsu.unite(p, p + 1);
p = dsu.find(p);
}
}
}
return 0;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> tc;
while (tc--) {
cin >> n >> m;
fill(pre, pre + n + 1, 0);
fill(suf + 1, suf + n + 2, n);
mx = 0;
mn = n;
for (int i = 1; i <= m; ++i) {
int L, R;
cin >> L >> R;
chkMax(pre[L], R);
chkMin(suf[L], R);
chkMax(mx, L);
chkMin(mn, R);
}
for (int i = 1; i <= n; ++i) chkMax(pre[i], pre[i - 1]);
for (int i = n; i; --i) chkMin(suf[i], suf[i + 1]);
fill(prv + 1, prv + n * 2 + 1, 0);
fill(mark + 1, mark + n + 1, false);
fill(fr + 1, fr + n * 2 + 1, 0);
int p = bfs();
if (!p) {
cout << "No\n";
continue;
}
while (p) {
if (p <= n) mark[p] = true;
prv[p] = fr[p];
p = fr[p];
}
cout << (bfs() ? "Yes" : "No") << '\n';
}
return 0;
}

浙公网安备 33010602011771号