首页 > 其他分享 >题解 QOJ837 / ZROI1287【Giant Penguin】

题解 QOJ837 / ZROI1287【Giant Penguin】

时间:2024-09-26 21:26:49浏览次数:8  
标签:rt Giant ZROI1287 smx int 题解 vis 100010 push

Petrozavodsk Winter 2020. Day 3. 300iq Contest 3. Problem G. Giant Penguin

Giant Penguin - Problem - QOJ.ac

题目描述

有一个 \(n\) 个点 \(m\) 条边的连通无向无权图,满足每个节点在至多 \(k\) 个简单环上(没有重复顶点的环是简单环)。\(q\) 次操作支持:1. 标记一个点; 2. 询问一个点到任意标记点的最短距离。

\(n, q\leq 1\times 10^5, m\leq 2n, k\leq 10\)。

solution

本题难点在于发现这是一道点分治题。

考虑任选一个节点 \(rt\),以它为根搜一棵 dfs 生成树。因为每个节点在至多 \(k\) 个简单环上,所以至多有 \(k\) 条返祖边(dfs 生成树没有横叉边),这是关键所在。考虑将这 \(k\) 条返祖边拿出来,以 \(k\) 条边各自任意的一个端点为关键点,那么图上两个点之间的最短路要么经过 \(k\) 个关键点,要么经过根,要么不走返祖边和根,第三者可以通过点分治使其成为子问题。然后根据自己的喜好写点分治或者点分树即可 \(O(nk\log n)\) 解决该问题。

以上省略的部分细节:

  1. 如果 dfs 生成树的根不为分治中心(这是大多数情况),那么会出现横叉边,只有连接由分治中心分割的两棵不同子树的横叉边需要在这层分治中处理。
  2. 点分树做法需要存储当前分治层的关键点到其它点的最短路。可以记录当前分治深度,同一深度的分治问题互不相交,可以存到同一个数组中,而分治深度显然不超过 \(O(\log n)\)(注意求重心要传入正确的子树大小以防深度超过 \(\log_2 n\))。当然离线点分治不会有这个问题。

code

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
int n, m, anc[100010], siz[100010], smx[100010], td[100010], tf[100010];
bool cut[100010];
vector<int> g[100010];
void dfs(int u, int fa) {/*{{{*/
  anc[u] = fa;
  for (int v : g[u]) if (!anc[v]) debug("%d -- %d\n", u, v), dfs(v, u);
}/*}}}*/
bool ontree(int u, int v) {/*{{{*/
  return anc[u] == v || anc[v] == u;
}/*}}}*/
int findcen(int u, int fa, int T) {/*{{{*/
  siz[u] = 1, smx[u] = 0;
  int rt = -1;
  for (int v : g[u]) if (!cut[v] && v != fa && ontree(u, v)) {
    int rs = findcen(v, u, T);
    if (rt == -1 || smx[rs] < smx[rt]) rt = rs;
    siz[u] += siz[v], smx[u] = max(smx[u], siz[v]);
  }
  smx[u] = max(smx[u], T - siz[u]);
  return rt == -1 || smx[u] < smx[rt] ? u : rt;
}/*}}}*/
int dis[100010][20][21], ans[100010][21], cnt[100010];
int vis[100010], tim, col[100010];
void bfs(int st, int dep, int id) {/*{{{*/
  queue<int> q;
  q.push(st);
  dis[st][dep][id] = 0;
  while (!q.empty()) {
    int u = q.front(); q.pop();
    for (int v : g[u]) if (vis[v] == tim && dis[v][dep][id] > dis[u][dep][id] + 1) {
      dis[v][dep][id] = dis[u][dep][id] + 1;
      q.push(v);
    }
  }
}/*}}}*/
void solve(int rt, int fa) {
  int dep = td[rt] = td[fa] + 1;
  assert(dep < 20);
  tf[rt] = fa;
  queue<int> q;
  vis[rt] = ++tim;
  col[rt] = -1;
  for (int v : g[rt]) if (!cut[v] && ontree(rt, v)) col[v] = v, q.push(v);
  while (!q.empty()) {
    int u = q.front(); q.pop();
    vis[u] = tim;
    for (int v : g[u]) if (!cut[v] && vis[v] != tim && ontree(u, v)) q.push(v), col[v] = col[u];
  }
  q.push(rt);
  vis[rt] = ++tim;
  vector<int> key{rt};
  while (!q.empty()) {
    int u = q.front(); q.pop();
    for (int v : g[u]) if (!cut[v]) {
      if (ontree(u, v)) { if (vis[v] == tim - 1) q.push(v), vis[v] = tim; }
      else if (vis[v] >= tim - 1 && col[u] != col[v]) key.push_back(u), key.push_back(v);
    }
  }
  {
    auto b = key.begin(), e = key.end();
    sort(b, e), key.erase(unique(b, e), e);
  }
  cnt[rt] = key.size();
  assert(cnt[rt] <= 21);
  debug("rt = %d\n", rt);
  for (int i = 0; i < cnt[rt]; i++) assert(vis[key[i]] == tim), bfs(key[i], dep, i), debug("key[%d] = %d\n", i, key[i]);
  cut[rt] = true;
  for (int v : g[rt]) if (!cut[v] && ontree(rt, v)) {
    findcen(v, rt, siz[v]);
    solve(findcen(v, rt, siz[v]), rt);
  }
}
void mdf(int u) {
  for (int p = u; p; p = tf[p]) {
    for (int i = 0; i < cnt[p]; i++) ans[p][i] = min(ans[p][i], dis[u][td[p]][i]);
  }
}
int qry(int u) {
  int res = 1e9;
  for (int p = u; p; p = tf[p]) {
    for (int i = 0; i < cnt[p]; i++) res = min(res, ans[p][i] + dis[u][td[p]][i]);
  }
  return res;
}
int main() {
#ifndef LOCAL
  cin.tie(nullptr)->sync_with_stdio(false);  
#endif
  memset(dis, 0x3f, sizeof dis);
  memset(ans, 0x3f, sizeof ans);
  int q, op, x;
  cin >> n >> m >> q;
  for (int i = 1, u, v; i <= m; i++) cin >> u >> v, g[u].push_back(v), g[v].push_back(u);
  dfs(1, -1);
  td[0] = -1;
  solve(findcen(1, 0, n), 0);
  cin >> q;
  while (q--) {
    cin >> op >> x;
    if (op == 1) mdf(x);
    else cout << qry(x) << endl;
  }
  return 0;
}

标签:rt,Giant,ZROI1287,smx,int,题解,vis,100010,push
From: https://www.cnblogs.com/caijianhong/p/18434399/solution-QOJ837

相关文章

  • 【Py/Java/C++三种语言OD独家2024E卷真题】20天拿下华为OD笔试之【哈希表】2024E-选修
    可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入输出示例一输入输出说明示例二输入输出说明解题思路代码pythonjavacpp时空复......
  • 【Py/Java/C++三种语言OD独家2024E卷真题】20天拿下华为OD笔试之【DFS/BFS】2024E-开
    可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入输出示例一输入输出说明示例二输入输出解题思路代码解法一:BFSpythonjavacpp......
  • LGP1313 题解
    原题链接:P1313[NOIP2011提高组]计算系数。难度:Easy。考察二项式定理的基本应用。正解发现存在式子\((ax+by)^k\),容易想到二项式定理。二项式定理:\[(x+y)^n=\sum\limits_{i=0}^{n}{n\choosei}x^iy^{n-i}\]令\(p=ax,q=by\),那么原式变为\((p+q)^k\)。那么此时......
  • LGB3717 题解
    原题链接:B3717组合数问题。难度:Easy组合数学的模板题。排除做法:\(n,m\le5\times10^6\),显然不能使用杨辉三角递推。模数为\(998,244,353\),无法使用\(\text{Lucas}\)定理。正解考虑直接使用组合数的计算式:\[{n\choosem}=\dfrac{n!}{m!(n-m)!}\]其中\(n!\)可......
  • P8908 [USACO22DEC] Palindromes P 题解
    P8908[USACO22DEC]PalindromesP题解算是好题,虽然没什么人做(简单地,我们考虑如何将一个字符串改变为回文串。显然如果我们判定所有\(\texttt{G}\)组成的是回文串,那么整个串一定是回文的。于是我们只考虑改变\(\texttt{G}\)的位置。那么由这类题的套路不难知道最优的变换......
  • 【洛谷】P1168 中位数 的题解
    【洛谷】P1168中位数的题解题目传送门题解不懂就问,这是什么标签啊,《线段树》《二分》《堆》《树状数组》qaq谔谔,教练讲的这是一题线段树,看半天看不出来,也不会做,只好去看题解。其他的题解讲什么主席树,平衡树,分块,结果看完第一篇人都傻了。什么东西嘛qaq(恼vector直接一......
  • CF1063E Lasers and Mirrors题解
    一道很好的手玩题,被薄纱了。首先判掉\(\foralli,p_i=i\)的情况(显然是\(n\))然后考虑按照\(p_i\)连边,先构造每一个环的方案。发现可以简单放置两面镜子使得\(i\)射到\(p_i\),而且只要从高到底构造,一定不会产生影响。但是每一个环的最后一个点很特殊,因为第1个点下面放置了让第1个......
  • LOJ 6241. 性能优化 I 题解
    题给代码意为\[\begin{align*}&\sum\limits_{i=1}^n\sum\limits_{j=1}^{\lfloor\fracni\rfloor}\sum\limits_{k=1}^j[\gcd(j,k)=1]\\=&\sum\limits_{i=1}^n\sum\limits_{j=1}^{\lfloor\fracni\rfloor}\varphi(j)\\......
  • P8475 「GLR-R3」雨水 题解
    关于这道题目卡\(O(n\logn)\)但是放\(O(n^2)\)我也是很疑惑。我们发现,题目要求的是字典序最小的序列。但凡涉及了字典序最小,答案或多或少的都会带点贪心思想。那我们也来贪一贪。考虑当前枚举到第\(i\)个点,如果后面有比它更小的数,那显然把它们交换过来是更优的。如果有多......
  • P8474 「GLR-R3」立春 题解
    俗话说的好:“打表出奇迹”,所以我们这一题打表计算。其实确实可以打表来找规律。通过打表,我们可以获得如下的结果:1 12 33 214 3155 9765…… ……然后观察可得:\[1\times3=1\times(2^2-1)=3\]\[3\times7=3\times(2^3-1)=21\]\[21\times15=21\t......