CF1702G2 Passable Paths (hard version)
2022/9/4 23:25:29
本文主要是介绍CF1702G2 Passable Paths (hard version),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Passable Paths (hard version)
给出一棵大小为 \(n\) 的树,\(q\) 次询问,每次给出一大小为 \(m\) 的点集,判断是否存在一条链覆盖这些点,注意这条链可以经过其他点。\(n,\sum m \leq 2\times 10^5\) ,\(q \leq 10^5\)。
SOLUTION1: 虚树
由于 \(q\) 次询问的 \(\sum m \le 2 \times 10^5\) ,那么我们可以考虑对于每次询问使用虚树来解决,将树浓缩为所有关键点以及这些点的 \(LCA\),然后通过 \(DFS\) 来判断每个点的儿子个数来判断是否是一条链即可。
具体的判断方法为:
- 首先虚树中是一定含有根节点的
- 然后我们判断是否 关键点中不含根节点 并且 根节点只连了一个点
- 如果满足上面的条件,那么我们就判断虚树中的非根节点是否有多于两个的非根节点出边,如果有,则不满足题目中的条件
- 如果不满足上面的条件,那么就看虚树中每个点是否有多于两个点的出边
#include <bits/stdc++.h> using namespace std; #define rep(i, b, s) for(int i = (b); i <= (s); ++ i) #define dec(i, b, s) for(int i = (b); i >= (s); -- i) using ll = long long; #ifdef LOCAL #include <debugger> #else #define debug(...) 42 #endif template <typename T> void chkmax(T &x, T y) { x = max(x, y); } template <typename T> void chkmin(T &x, T y) { x = min(x, y); } constexpr int N = 2E5 + 10; vector<int> son[N]; vector<int> g[N]; int tp, a[N], use[N]; bool ans, has_root, f; int dfn[N], depth[N], sz[N], hson[N], top[N], parent[N]; void dfs1(int u, int fa, int d) { depth[u] = d; sz[u] = 1; parent[u] = fa; for (int &v: son[u]) if (v != fa) { dfs1(v, u, d + 1); sz[u] += sz[v]; if (hson[u] == -1 || sz[hson[u]] < sz[v]) hson[u] = v; } } void dfs2(int u, int id) { top[u] = id; dfn[u] = ++ tp; if (hson[u] == 0) return ; dfs2(hson[u], id); for (int &v: son[u]) if (v != parent[u] && v != hson[u]) { dfs2(v, v); } } int lca(int u, int v) { while (top[u] != top[v]) { if (depth[top[u]] > depth[top[v]]) { u = parent[top[u]]; } else { v = parent[v]; } } return depth[u] < depth[v] ? u : v; } void dfs3(int u, int fa) { int now = 0; for (int &v: g[u]) { if (v != fa) dfs3(v, u); if (!f || v != 1) { ++ now; } } if (!(f && u == 1) && now > 2) { ans = true; } g[u].clear(); } void solve() { int n; cin >> n; for (int i = 1; i < n; i ++ ) { int u, v; cin >> u >> v; son[u].emplace_back(v); son[v].emplace_back(u); } dfs1(1, 0, 1); dfs2(1, 1); int q; cin >> q; while (q -- ) { int k; cin >> k; has_root = false; for (int i = 1; i <= k; i ++ ) { cin >> a[i]; if (a[i] == 1) has_root = true; } sort(a + 1, a + 1 + k, [&] (int x, int y){ return dfn[x] < dfn[y]; }); auto add = [&] (int u, int v) { g[u].emplace_back(v); g[v].emplace_back(u); }; vector<int> stk{1}; for (int i = 1; i <= k; i ++ ) { if (a[i] != 1) { int p = lca(a[i], stk.back()); if (p != stk.back()) { while (dfn[p] < dfn[stk[(int)stk.size() - 2]]) { add(stk.back(), stk[(int)stk.size() - 2]); stk.pop_back(); } add(p, stk.back()), stk.pop_back(); if (dfn[p] > dfn[stk.back()]) stk.emplace_back(p); } stk.emplace_back(a[i]); } } while (stk.size() > 1) { if (stk.back() != 1) { add(stk.back(), stk[(int)stk.size() - 2]); stk.pop_back(); } } f = (!has_root && (int)g[1].size() == 1); // cout << (f ? "TRUE" : "FALSE") << "\n"; // 表示关键点里面没有根,并且根(一定存在)在虚树中只连了一个点 dfs3(1, 0); if (ans) { cout << "NO\n"; } else { cout << "YES\n"; } ans = false; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T = 1; // cin >> T; while(T -- ) solve(); return 0; }
SOLUTION2:暴力判链
在树上判断三个点 \((x,y,z)\) 是否在一条脸上的方法是,判断 \(\{dis(x,y), dis(y,z), dis(x,z)\}\) 中两条短边的和是否等于最长边。
那么我们可以维护两个点 \((x,y)\) 为链的两端点,然后依次枚举剩余的点,每次更新 \((x,y)\) 即可。
#include <bits/stdc++.h> using namespace std; using ll = long long; #ifdef LOCAL #include <debugger> #else #define debug(...) 42 #endif template <typename T> void chkmax(T &x, T y) { x = max(x, y); } template <typename T> void chkmin(T &x, T y) { x = min(x, y); } const int N = 2e5 + 10; vector<int> son[N]; ll ans[N]; int d[N]; int depth[N]; int fa[N][20]; int q[N]; void bfs(int root) { memset(depth, 0x3f, sizeof depth); depth[0] = 0, depth[root] = 1; int hh = 0, tt = 0; q[0] = root; while(hh <= tt) { int u = q[hh ++ ]; int num = son[u].size(); for(int i = 0; i < num; i ++ ) { int v = son[u][i]; if(depth[v] > depth[u] + 1) { // father[v] = u; depth[v] = depth[u] + 1; fa[v][0] = u; for(int k = 1; k < 20; k ++ ) { fa[v][k] = fa[fa[v][k - 1]][k - 1]; } q[++ tt] = v; } } } } int lca(int a, int b) { if(depth[a] < depth[b]) swap(a, b); for(int k = 19; k >= 0; k -- ) { if(depth[fa[a][k]] >= depth[b]) { a = fa[a][k]; } } if(a == b) return a; for(int k = 19; k >= 0; k -- ) { if(fa[a][k] != fa[b][k]) { a = fa[a][k]; b = fa[b][k]; } } return fa[a][0]; } void solve() { int n; cin >> n; vector<int> a(n); for(int &x: a) cin >> x; //mp[x] ++; if(n == 1) { cout << "YES\n"; return ; } int x = a[0], y = a[1]; auto dis = [&] (int X, int Y) { return depth[X] + depth[Y] - 2 * depth[lca(X, Y)]; }; for(int i = 2; i < n; i ++ ) { int z = a[i]; vector<int> l(3); l[0] = dis(x, y), l[1] = dis(y, z), l[2] = dis(x, z); auto r = l; sort(l.begin(), l.end()); if(l[0] + l[1] != l[2]) { cout << "NO\n"; return ; } if(r[1] == l.back()) { x = z; } else if(r[2] == l.back()) { y = z; } } cout << "YES\n"; } int main() { cin.tie(nullptr)->sync_with_stdio(false); int n; cin >> n; for(int i = 1; i < n; i ++ ) { int u, v; cin >> u >> v; son[u].push_back(v); son[v].push_back(u); } bfs(1); int Q; cin >> Q; while(Q -- ) solve(); return 0; }
这篇关于CF1702G2 Passable Paths (hard version)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享
- 2024-11-22ansible 的archive 参数是什么意思?-icode9专业技术文章分享
- 2024-11-22ansible 中怎么只用archive 排除某个目录?-icode9专业技术文章分享
- 2024-11-22exclude_path参数是什么作用?-icode9专业技术文章分享
- 2024-11-22微信开放平台第三方平台什么时候调用数据预拉取和数据周期性更新接口?-icode9专业技术文章分享
- 2024-11-22uniapp 实现聊天消息会话的列表功能怎么实现?-icode9专业技术文章分享
- 2024-11-22在Mac系统上将图片中的文字提取出来有哪些方法?-icode9专业技术文章分享
- 2024-11-22excel 表格中怎么固定一行显示不滚动?-icode9专业技术文章分享
- 2024-11-22怎么将 -rwxr-xr-x 修改为 drwxr-xr-x?-icode9专业技术文章分享
- 2024-11-22在Excel中怎么将小数向上取整到最接近的整数?-icode9专业技术文章分享