ARC181E Min and Max at the edge
先考虑怎么判断一个图是好图。感性上我们选取的区间一定是比较小的,因此考虑以 \(|u-v|\) 为边权求一棵最小生成树。不难说明,如果图是好图,那么这样找出来的就是它的唯一一棵合法生成树。
我们先求出原图的生成树。如果割掉的边是非树边,那么直接判就好了。考虑割掉树边的情况。
不妨设断掉了边 \((u,v)\),那么整个图被分成了两个连通块,连通块之间多了若干条非树边,形如 \([l_1,r_1],[l_2,r_2],\cdots,[l_k,r_k]\),这里直接写成了区间的形式。
假设我们新换上了区间 \([l_1,r_1]\),那么需要满足 \(l_1=\max l\) 和 \(r_1=\min r\)。如果不存在这样的区间那么割掉 \((u,v)\) 就不合法。
然后我们考虑剩下的非树边会不会满足条件。称一条非树边 \((x,y)\) 是合法的当且仅当树上 \(x\to y\) 的路径上每个点都在 \(x\) 和 \(y\) 之间。
如果我们换上了一条合法的边,在判掉上面的部分后,我们可以说明其它非树边的合法性是不会改变的。
设非树边为 \((x,y)\),我们割掉了 \((u,v)\),换上了 \((u',v')\)。那么一侧的路径就从 \(x\to u\) 变成了 \(x\to u'\)。因为 \((x,y)\) 代表的区间是包含了 \((u',v')\),并且原树上 \((u',v')\) 合法,所以路径 \(x\to u\to u'\) 是和 \(x\to u'\) 等价的。而 \(u\to u'\) 和 \(v\to v'\) 两条路径也和 \((u',v')\) 边等效,上面已经判了 \((u',v')\) 包含于 \((x,y)\),所以 \(x\to u\to u'\) 也等价于 \(x\to u\),即 \(x\to u\) 和 \(x\to u'\) 是等价的。(是不是说的有点绕口令……)
总之只有在原树所有非树边都是合法时,换上一条合法边才有可能合法。
现在我们考虑换上了不合法边的情况。如果新图是好图,那么所有的不合法边在新图上需要变成合法。这说明我们断掉边 \((u,v)\) 后所有原来的不合法边都要是连接两个连通块的。那么此时我们换上来的不合法边就是其中边权最小的边。这说明我们换上的不合法边其实是唯一的!
设那一条可能换上的不合法边 \((x,y)\),取出原树上 \(x\to y\) 的链 \(x=S_1\to S_2\to\cdots\to S_k=y\),每个点属于其中某个 \(S_i\) 的子树。割掉的边 \((u,v)\) 也一定是链上的一条边。
对于一条原来不合法的边 \((x',y')\),设 \(x'\) 和 \(y'\) 所属的子树分别为 \(i,j\) 且 \(i\le j\)。割掉的边 \((u,v)\) 需要在原树 \(x'\to y'\) 的路径上,即是 \(i\) 到 \(j\) 区间内的一条边。
对于原来一条合法的边,如果它们在某一棵子树内就不用管了。否则如果断掉了 \(i\) 和 \(j\) 之间的一条边,那么新的路径就是 \(x'\to S_i\to x\) 和 \(y'\to S_j\to y\),这个可以预处理之后判一下。如果不合法了就说明不能断 \(i\) 到 \(j\) 之间的边,否则就是没有限制的。
所以我们要进行的就是对区间里的边禁用,差分一下即可。那么总复杂度就是 \(O(n\log n)\)。
感觉细节有点多,代码写得非常混乱。
顺便提一下标算的做法:我们不一定要按照 \(|u-v|\) 排序,只要保证被包含的区间要在前面就好。因此一个考虑是按 \((-u,v)\) 做最小生成树。同理我们还可以用 \((v,-u)\) 做。事实上两棵生成树相同就是对的了,但是好像不太会证。之后就是简单的了。
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#define eb emplace_back
using namespace std;
using pii = pair<int, int> ;
template <typename T> void Chkmin(T &x, T y) { x = min(x, y); }
template <typename T> void Chkmax(T &x, T y) { x = max(x, y); }
const int kN = 2e5 + 5;
int n, m;
int u[kN], v[kN], ord[kN];
bool mst[kN], ans[kN], flag[kN];
vector<pii> g[kN];
struct DSU {
int fa[kN];
DSU() { iota(fa, fa + kN, 0); }
int Find(int x) {
return (fa[x] == x) ? x : (fa[x] = Find(fa[x]));
}
bool Merge(int x, int y) {
x = Find(x);
y = Find(y);
if(x == y) return 0;
return fa[x] = y, 1;
}
}dsu;
int fa[kN], jp[kN], dep[kN], mn[kN], mx[kN], fid[kN];
void DFS(int x, int fa) {
::fa[x] = fa;
dep[x] = dep[fa] + 1;
int f1 = jp[fa], f2 = jp[f1];
if(dep[f1] * 2 == dep[fa] + dep[f2]) {
jp[x] = f2;
mn[x] = min({x, mn[fa], mn[f1]});
mx[x] = max({x, mx[fa], mx[f1]});
}else {
jp[x] = fa;
mn[x] = mx[x] = x;
}
for(pii k : g[x]) {
int to, id;
tie(to, id) = k;
if(to != fa) fid[to] = id, DFS(to, x);
}
}
bool In(int x, int y) {
while(dep[y] > dep[x]) y = (dep[jp[y]] >= dep[x]) ? jp[y] : fa[y];
return x == y;
}
tuple<int, int, int> LCA(int x, int y) {
int mn = n + 1, mx = 0;
if(dep[x] < dep[y]) swap(x, y);
while(dep[x] > dep[y]) {
if(dep[jp[x]] >= dep[y]) {
Chkmin(mn, ::mn[x]);
Chkmax(mx, ::mx[x]);
x = jp[x];
}else {
Chkmin(mn, x);
Chkmax(mx, x);
x = fa[x];
}
}
while(x != y) {
if(jp[x] != jp[y]) {
Chkmin(mn, min(::mn[x], ::mn[y]));
Chkmax(mx, max(::mx[x], ::mx[y]));
x = jp[x], y = jp[y];
}else {
Chkmin(mn, min(x, y));
Chkmax(mx, max(x, y));
x = fa[x], y = fa[y];
}
}
Chkmin(mn, x);
Chkmax(mx, x);
return make_tuple(mn, mx, x);
}
void Path(int u, int v, vector<int> &np, vector<int> &ep) {
vector<int> fu, fv;
vector<int> eu, ev;
while(u != v) {
if(dep[u] >= dep[v]) {
fu.eb(u);
eu.eb(fid[u]);
u = fa[u];
}else {
fv.eb(v);
ev.eb(fid[v]);
v = fa[v];
}
}
fu.eb(u);
reverse(fv.begin(), fv.end());
reverse(ev.begin(), ev.end());
fu.insert(fu.end(), fv.begin(), fv.end());
eu.insert(eu.end(), ev.begin(), ev.end());
np = fu, ep = eu;
}
struct PQ {
__gnu_pbds::priority_queue<int> q1, q2;
void Push(int x) { q1.push(x); }
void Erase(int x) { q2.push(x); }
bool Empty() { return q1.size() == q2.size(); }
int Top() {
while(q2.size() && (q1.top() == q2.top())) q1.pop(), q2.pop();
return q1.top();
}
void Join(PQ &rhs) {
q1.join(rhs.q1);
q2.join(rhs.q2);
}
};
int chg[kN];
map<pii, int> mp;
PQ ql[kN], qr[kN];
vector<pii> updl[kN], updr[kN];
void DFS2(int x, int fa, int eid) {
for(pii k : g[x]) {
int to, id;
tie(to, id) = k;
if(to == fa) continue;
DFS2(to, x, id);
ql[x].Join(ql[to]);
qr[x].Join(qr[to]);
}
for(pii k : updl[x]) {
int v, f;
tie(v, f) = k;
if(f) ql[x].Push(v);
else ql[x].Erase(v), ql[x].Erase(v);
}
for(pii k : updr[x]) {
int v, f;
tie(v, f) = k;
if(f) qr[x].Push(v);
else qr[x].Erase(v), qr[x].Erase(v);
}
if(eid && !ql[x].Empty()) {
int l = ql[x].Top(), r = -qr[x].Top();
auto it = mp.find(pii {l, r});
if(it != mp.end()) {
int id = it -> second;
if(!mst[id]) {
bool flag = 0;
bool fu = In(x, u[id]), fv = In(x, v[id]);
chg[eid] = (fu ^ fv) * id;
}
}
}
}
int bel[kN];
int d[kN];
bool inp[kN];
void DFS3(int x, int fa, int b) {
bel[x] = b;
for(pii k : g[x]) {
int to = k.first;
if((to != fa) && !inp[to]) DFS3(to, x, b);
}
}
int mnu[kN], mxu[kN], mnv[kN], mxv[kN];
void DFS4(int x, int fa, int *mn, int *mx) {
if(!fa) mn[x] = mx[x] = x;
else {
Chkmin(mn[x], x);
Chkmax(mx[x], x);
}
for(pii k : g[x]) {
int to = k.first;
if(to != fa) {
mn[to] = mn[x];
mx[to] = mx[x];
DFS4(to, x, mn, mx);
}
}
}
int main() {
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i++) {
cin >> u[i] >> v[i];
if(u[i] > v[i]) swap(u[i], v[i]);
mp[pii {u[i], v[i]}] = i;
}
iota(ord + 1, ord + m + 1, 1);
sort(ord + 1, ord + m + 1,
[&](int x, int y) { return v[x] - u[x] < v[y] - u[y]; });
for(int i = 1; i <= m; i++) {
int e = ord[i];
if(mst[e] = dsu.Merge(u[e], v[e])) {
g[u[e]].eb(v[e], e);
g[v[e]].eb(u[e], e);
}
}
DFS(1, 0);
int cnt0 = 0, id = 0;
for(int i = m; i >= 1; i--) {
int e = ord[i];
if(mst[e]) continue;
int mn, mx, lca;
tie(mn, mx, lca) = LCA(u[e], v[e]);
int l = u[e], r = v[e];
flag[e] = ((mn == l) && (mx == r));
if(!flag[e]) cnt0++, id = e;
updl[l].eb(l, 1);
updl[r].eb(l, 1);
updl[lca].eb(l, 0);
updr[l].eb(-r, 1);
updr[r].eb(-r, 1);
updr[lca].eb(-r, 0);
}
if(cnt0 <= 1) {
for(int i = 1; i <= m; i++) {
if(!mst[i]) ans[i] = (!cnt0 || (i == id));
}
}
DFS2(1, 0, 0);
if(!cnt0) {
for(int i = 1; i <= m; i++) {
if(mst[i]) ans[i] = (chg[i] != 0);
}
for(int i = 1; i <= m; i++) {
cout << (ans[i] ? "Yes\n" : "No\n");
}
return 0;
}
vector<int> np, ep;
Path(u[id], v[id], np, ep);
for(int i : np) inp[i] = 1;
for(int i = 0; i < np.size(); i++) DFS3(np[i], 0, i);
DFS4(u[id], 0, mnu, mxu);
DFS4(v[id], 0, mnv, mxv);
for(int i = 1; i <= m; i++) {
if(mst[i] || (i == id)) continue;
int bu = bel[u[i]];
int bv = bel[v[i]];
if(bu == bv) {
if(flag[i]) continue;
d[0]++;
break;
}
int l = u[i], r = v[i];
if(bu > bv) swap(u[i], v[i]), swap(bu, bv);
int mn = min(mnu[u[i]], mnv[v[i]]);
int mx = max(mxu[u[i]], mxv[v[i]]);
if(!flag[i]) d[0]++, d[bu]--, d[bv]++;
if((mn != l) || (mx != r)) {
if(!flag[i]) { d[0]++; break; }
else d[bu]++, d[bv]--;
}
}
for(int i = 1; i < np.size(); i++) d[i] += d[i - 1];
for(int i = 0; i + 1 < np.size(); i++) {
ans[ep[i]] = ((chg[ep[i]] == id) && !d[i]);
}
for(int i = 1; i <= m; i++) {
cout << (ans[i] ? "Yes\n" : "No\n");
}
return 0;
}
浙公网安备 33010602011771号