怎么给提高组小登会从绿题放到这种题,我们 [] 真是蒸蒸日上。
太厉害的题了,完全想不到。
题意:很简单了,不再赘述。
做法:
先不管删边的事情,我们先思考如何对一个图构造一棵树出来。
感觉对于这个 \((u,v)\) 的限制很有一种最小生成树的感觉,但是因为是对点的限制不对边所以完全没法做,但是我们考虑可以对边适当的赋权。那么为了表示点的限制,我们不妨把编号也映射到一个 \(A\) 数组上,同时保持了他们的偏序关系。我们会根据接下来的分析具体地给出我们对 \(A\) 数组的要求和一种简单的构造。
然后我们考虑把限制转到边上,我们希望 \(\forall k\in path(u,v),A_u\le A_k\le A_v\),那么把限制转到边上,假设中间有一条边 \((x,y)\),那么就是 \(A_u\le A_x,A_y\le A_v\),那么我们就要 \(w(x,y)< w(u,v)\),\(w\) 是这条边的权,我们直接取 \(A = x^i,w(u,v) = |A_u-A_v|\),这样去对整个图重新进行标权,跑出来的最小生成树就是唯一且合法的构造。
但是我们在实际写的时候肯定不能这么写,我们没法对这么大的数去做,但是我们注意到,只要满足对于 \(u\le x< y\le v\) 都有 \(w(u,v)>w(x,y)\) 那么就可以了,所以我们考虑令 \(w(u,v)=(n+1)v-u\),这样跑出来一个生成树就可以保证我路径上最大值一定是 \(v\),如果大一权都会大很多。
但是随之而来有一个问题,这样跑出来的 \(u\) 不一定是最小的,因为我可能连续的小 \(v\) 连在一起这样边权小一点,所以我们需要检验 \(u\) 也就是最小值的限制。我们只需要把边权改成 \(w(u,v)=(n+1-u)(n+1)-(n+1-v)\) 再跑一遍就可以了。最后判定是否可以找到一个树就直接看两个树跑出来的边集是否相同就可以了。
判断边集可以用 +/xor hash,但是还有删边的问题。其实也不难,就是我们考虑先跑出来一个生成树,如果不是树边,就直接用现在的就可以了;否则删掉之后找一条最小的去连通即可。这个东西可以是拍成 dfn 序然后变成平面问题或者用并查集合并边也可以。
代码:
别问我为什么我两种做法都写了,问题就是我 dfn 调不出来所以换成并查集,然后突然反应过来我 inf 开小了。
dfn 序:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 5e5 + 5;
mt19937 rnd(time(0));
struct Edge {int u, v, w, val, id;friend bool operator<(Edge x, Edge y) {return x.w < y.w;}
} e[maxn];
vector<int> g[maxn];
int n, m, use[maxn], ans[2][maxn];
struct dsu {int pre[maxn];void init() {for (int i = 1; i <= n; i++)pre[i] = i;}int fnd(int x) {return (pre[x] == x ? x : pre[x] = fnd(pre[x]));}
} tree1;
struct node {int mn, val;friend node operator+(node x, node y) {return (x.mn < y.mn ? x : y);}
} ;
struct Segtree {node tr[maxn << 2];void pushup(int t) {tr[t] = tr[t << 1] + tr[t << 1 | 1];}void build(int l, int r, int t) {if(l == r) {tr[t] = {(int)9e18, 0};return ;}int mid = l + r >> 1;build(l, mid, t << 1), build(mid + 1, r, t << 1 | 1);pushup(t);}void modify(int l, int r, int pos, int t, node val) {if(l == r) {tr[t] = tr[t] + val;return ;}int mid = l + r >> 1;if(pos <= mid)modify(l, mid, pos, t << 1, val);elsemodify(mid + 1, r, pos, t << 1 | 1, val);pushup(t);}node query(int l, int r, int x, int y, int t) {if(x <= l && r <= y)return tr[t];int mid = l + r >> 1;if(y <= mid)return query(l, mid, x, y, t << 1);if(mid < x)return query(mid + 1, r, x, y, t << 1 | 1);return query(l, mid, x, y, t << 1) + query(mid + 1, r, x, y, t << 1 | 1);}
} tree;
int sz[maxn], dfn[maxn], tot, f[maxn];
node val[maxn];
void dfs(int u, int fa) {sz[u] = 1; dfn[u] = ++tot;f[u] = fa;for (int i = 0; i < g[u].size(); i++) {int v = g[u][i];if(v == fa)continue;dfs(v, u);sz[u] += sz[v];}
}
struct Value {int x, y, w, val;friend bool operator<(Value x, Value y) {return x.y < y.y;}
} ;
vector<Value> vec;
int pos[maxn];
vector<int> qry[maxn];
void kruskal(int k) {for (int i = 1; i <= m; i++)use[i] = 0;sort(e + 1, e + m + 1);for (int i = 1; i <= n; i++)g[i].clear();tree1.init();int res = 0;int cntt = 0;for (int i = 1; i <= m; i++) {int x = tree1.fnd(e[i].u), y = tree1.fnd(e[i].v);if(x == y)continue;use[i] = 1, res ^= e[i].val;tree1.pre[x] = y;g[e[i].u].push_back(e[i].v);g[e[i].v].push_back(e[i].u);cntt++;}if(cntt != n - 1)exit(1);tot = 0;dfs(1, 0);vec.clear();for (int i = 1; i <= m; i++) {if(use[i])continue;vec.push_back(Value{dfn[e[i].u], dfn[e[i].v], e[i].w, e[i].val});vec.push_back(Value{dfn[e[i].v], dfn[e[i].u], e[i].w, e[i].val});}sort(vec.begin(), vec.end());for (int i = 1; i <= n; i++)qry[i].clear();for (int i = 1; i <= m; i++) {if(use[i]) {if(f[e[i].v] == e[i].u)swap(e[i].u, e[i].v);qry[dfn[e[i].u] - 1].push_back(i);// cout << dfn[e[i].u] << " " << e[i].u << " " << e[i].v << " " << e[i].id << endl;}}int cur = 0;tree.build(1, n, 1);for (int i = 1; i <= m; i++)val[i] = node{(int)9e18, 0};for (int i = 1; i <= n; i++) {// cout << i << " adf" << endl;while(cur < vec.size() && vec[cur].y == i)tree.modify(1, n, vec[cur].x, 1, node{vec[cur].w, vec[cur].val}), cur++;for (int j = 0; j < qry[i].size(); j++)val[qry[i][j]] = val[qry[i][j]] + tree.query(1, n, dfn[e[qry[i][j]].u], dfn[e[qry[i][j]].u] + sz[e[qry[i][j]].u] - 1, 1);}tree.build(1, n, 1);cur--;for (int i = 1; i <= n; i++)qry[i].clear();for (int i = 1; i <= m; i++) {if(use[i]) qry[dfn[e[i].u] + sz[e[i].u]].push_back(i);}for (int i = n; i >= 1; i--) {while(cur >= 0 && vec[cur].y == i)tree.modify(1, n, vec[cur].x, 1, node{vec[cur].w, vec[cur].val}), cur--;for (int j = 0; j < qry[i].size(); j++)val[qry[i][j]] = val[qry[i][j]] + tree.query(1, n, dfn[e[qry[i][j]].u], dfn[e[qry[i][j]].u] + sz[e[qry[i][j]].u] - 1, 1);}for (int i = 1; i <= m; i++) {if(val[i].val || !use[i])ans[k][e[i].id] = (!use[i] ? res : (res ^ e[i].val ^ val[i].val));}
}
signed main() {
// freopen("test.in", "r", stdin);
// freopen("baoli.out", "w", stdout);cin >> n >> m;for (int i = 1; i <= m; i++)cin >> e[i].u >> e[i].v, e[i].val = rnd(), e[i].id = i;for (int i = 1; i <= m; i++)e[i].w = e[i].v * (n + 1) - e[i].u;kruskal(0);for (int i = 1; i <= m; i++) {if(e[i].u > e[i].v)swap(e[i].u, e[i].v);e[i].w = (n + 1 - e[i].u) * (n + 1) - (n + 1 - e[i].v);}kruskal(1);for (int i = 1; i <= m; i++)cout << (ans[1][i] == ans[0][i] && ans[1][i] && ans[0][i] ? "Yes" : "No") << endl;return 0;
}
并查集:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 5e5 + 5;
mt19937 rnd(time(0));
struct Edge {int u, v, w, val, id;friend bool operator<(Edge x, Edge y) {return x.w < y.w;}
} e[maxn];
vector<int> g[maxn];
int n, m, use[maxn], ans[2][maxn];
struct dsu {int pre[maxn];void init() {for (int i = 1; i <= n; i++)pre[i] = i;}int fnd(int x) {return (pre[x] == x ? x : pre[x] = fnd(pre[x]));}
} tree1;
struct node {int mn, val;friend node operator+(node x, node y) {return (x.mn < y.mn ? x : y);}
} ;
int sz[maxn], dfn[maxn], tot, f[maxn], dep[maxn];
node val[maxn];
void dfs(int u, int fa) {sz[u] = 1; dfn[u] = ++tot;dep[u] = dep[fa] + 1;f[u] = fa;for (int i = 0; i < g[u].size(); i++) {int v = g[u][i];if(v == fa)continue;dfs(v, u);sz[u] += sz[v];}
}
struct Value {int x, y, w, val;friend bool operator<(Value x, Value y) {return x.y < y.y;}
} ;
vector<Value> vec;
int pos[maxn], id[maxn];
vector<int> qry[maxn];
void kruskal(int k) {for (int i = 1; i <= m; i++)use[i] = 0;sort(e + 1, e + m + 1);for (int i = 1; i <= n; i++)g[i].clear();tree1.init();int res = 0;int cntt = 0;for (int i = 1; i <= m; i++) {int x = tree1.fnd(e[i].u), y = tree1.fnd(e[i].v);if(x == y)continue;use[i] = 1, res ^= e[i].val;tree1.pre[x] = y;g[e[i].u].push_back(e[i].v);g[e[i].v].push_back(e[i].u);cntt++;}if(cntt != n - 1)exit(1);tot = 0;dfs(1, 0);for (int i = 1; i <= m; i++) {if(use[i]) {if(f[e[i].u] == e[i].v)swap(e[i].u, e[i].v);id[e[i].v] = i;}}vec.clear();tree1.init();for (int i = 1; i <= m; i++) {if(use[i])continue;int x = tree1.fnd(e[i].u), y = tree1.fnd(e[i].v);ans[k][e[i].id] = res;while(x != y) {if(dep[tree1.fnd(f[x])] < dep[tree1.fnd(f[y])])swap(x, y);ans[k][e[id[x]].id] = res ^ e[id[x]].val ^ e[i].val;// cout << e[id[x]].id << "adsfdf " << x << " " << id[x] << endl;tree1.pre[x] = tree1.fnd(f[x]);x = tree1.fnd(x);}}
}
signed main() {
// freopen("test.in", "r", stdin);
// freopen("baoli.out", "w", stdout);cin >> n >> m;for (int i = 1; i <= m; i++)cin >> e[i].u >> e[i].v, e[i].val = rnd(), e[i].id = i;for (int i = 1; i <= m; i++)e[i].w = e[i].v * (n + 1) - e[i].u;kruskal(0);for (int i = 1; i <= m; i++) {if(e[i].u > e[i].v)swap(e[i].u, e[i].v);e[i].w = (n + 1 - e[i].u) * (n + 1) - (n + 1 - e[i].v);}kruskal(1);for (int i = 1; i <= m; i++)cout << (ans[1][i] == ans[0][i] && ans[1][i] && ans[0][i] ? "Yes" : "No") << endl;return 0;
}