首页 > 其他分享 >P9058 [Ynoi2004] rpmtdq 题解

P9058 [Ynoi2004] rpmtdq 题解

时间:2024-04-05 15:56:17浏览次数:11  
标签:std P9058 int 题解 rpmtdq kMaxN stk top dis

Description

给定一棵有边权的无根树,需要回答一些询问。

定义 \(\texttt{dist(i,j)}\) 代表树上点 \(i\) 和点 \(j\) 之间的距离。

对于每一组询问,会给出 \(l,r\),你需要输出 \(\min(\texttt{dist(i,j)})\) 其中 \(l\leq i < j \leq r\)。\(n\leq2\times 10^5\),\(q\leq 10^6\),\(1\le z\le 10^9\)。

Solution

注意到一个点对 \([l,r]\) 能作为询问的答案,当且仅当 \(\min(\texttt{dist(i,j)})=\texttt{dist(i,j)}(l\leq i<j\leq r)\),考虑求出所有这样的 \([l,r]\),然后二维数点即可。

先点分治,假设分治中心为 \(x\),\(i\) 到 \(x\) 的距离为 \(a_i\)。

那么 \(\texttt{dist(i,j)}=a_i+a_j\),则如果 \(i<k<j\) 且 \(\texttt{dist(i,k)}\geq \texttt{dist(i,j)},\texttt{dist(k,j)}\geq \texttt{dist(i,j)}\),\([i,j]\) 就可以加到重要区间内。

化简一下就是 \(\max\left\{a_i,a_j\right\}\leq \min_{k=i+1}^{j-1}{a_k}\),容易发现如果按照 \(a\) 从小到大加点,那么加入 \(j\) 时,\(i\) 一定是 \(j\) 的前驱或后继,用 set 维护即可。

时间复杂度:\(O(n\log^2n+q\log n)\)。

但是这样做常数太大了,过不了,因此点分治部分可以先按照编号大小排序,然后正反扫两边,维护单调栈即可。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5, kMaxQ = 1e6 + 5;

int n, q, rt;
int mx[kMaxN], sz[kMaxN], stk[kMaxN];
int64_t dis[kMaxN], ans[kMaxQ];
bool del[kMaxN];
std::vector<int> vec;
std::vector<std::pair<int, int64_t>> qq[kMaxN];
std::vector<std::pair<int, int>> G[kMaxN], qr[kMaxN];

struct BIT {
  int64_t c[kMaxN];

  BIT() { memset(c, 0x3f, sizeof(c)); }

  void upd(int x, int64_t v) {
    for (; x; x -= x & -x) c[x] = std::min(c[x], v);
  }

  int64_t qry(int x) {
    int64_t ret = 1e18;
    for (; x <= n; x += x & -x) ret = std::min(ret, c[x]);
    return ret;
  }
} bit;

void getsz(int u, int fa) {
  mx[u] = 0, sz[u] = 1;
  for (auto [v, w] : G[u]) {
    if (v == fa || del[v]) continue;
    getsz(v, u);
    sz[u] += sz[v], mx[u] = std::max(mx[u], sz[v]);
  }
}

void getrt(int u, int fa, int tot) {
  mx[u] = std::max(mx[u], tot - sz[u]);
  if (mx[u] < mx[rt]) rt = u;
  for (auto [v, w] : G[u]) {
    if (v == fa || del[v]) continue;
    getrt(v, u, tot);
  }
}

void dfs2(int u, int fa) {
  vec.emplace_back(u);
  for (auto [v, w] : G[u]) {
    if (v == fa || del[v]) continue;
    dis[v] = dis[u] + w;
    dfs2(v, u);
  }
}

void dfs1(int u) {
  dis[u] = 0, dfs2(u, 0);
  std::sort(vec.begin(), vec.end());
  int top = 0;
  for (auto x : vec) {
    for (; top && dis[stk[top]] > dis[x]; --top) {}
    if (top) qq[x].emplace_back(stk[top], dis[x] + dis[stk[top]]);
    stk[++top] = x;
  }
  top = 0;
  std::reverse(vec.begin(), vec.end());
  for (auto x : vec) {
    for (; top && dis[stk[top]] > dis[x]; --top) {}
    if (top) qq[stk[top]].emplace_back(x, dis[x] + dis[stk[top]]);
    stk[++top] = x;
  }
  vec.clear(), vec.shrink_to_fit();
  del[u] = 1;

  for (auto [v, w] : G[u]) {
    if (del[v]) continue;
    rt = 0, getsz(v, 0), getrt(v, 0, sz[v]), dfs1(rt);
  }
}

void solve() {
  for (int r = 1; r <= n; ++r) {
    for (auto [l, w] : qq[r]) bit.upd(l, w);
    for (auto [l, id] : qr[r]) ans[id] = bit.qry(l);
  }
}

void dickdreamer() {
  std::cin >> n;
  for (int i = 1; i < n; ++i) {
    int u, v, w;
    std::cin >> u >> v >> w;
    G[u].emplace_back(v, w), G[v].emplace_back(u, w);
  }
  mx[0] = 1e9, getsz(1, 0), getrt(1, 0, n), dfs1(rt);
  std::cin >> q;
  for (int i = 1; i <= q; ++i) {
    int l, r;
    std::cin >> l >> r;
    ans[i] = 1e18;
    if (l != r) qr[r].emplace_back(l, i);
    else ans[i] = -1;
  }
  solve();
  for (int i = 1; i <= q; ++i) std::cout << ans[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:std,P9058,int,题解,rpmtdq,kMaxN,stk,top,dis
From: https://www.cnblogs.com/Scarab/p/18115792

相关文章

  • 二叉树计算【华为OD机试JAVA&Python&C++&JS题解】
    一.题目-二叉树计算给出一个二叉树如下图所示:6/79\/-26请由该二叉树生成一个新的二叉树,它满足其树中的每个节点将包含原始树中的左子树和右子树的和。20(7-2+9+6)/\-26\/......
  • 学生重新排队【华为OD机试JAVA&Python&C++&JS题解】
    一.题目-学生重新排队n个学生排成一排,学生编号分别是1到n,n为3的整倍数。老师随机抽签决定将所有学生分成m个3人的小组,n=3*m为了便于同组学生交流,老师决定将小组成员安排到一起,也就是同组成员彼此相连,同组任意两个成员输入描述:之间无其它组的成员。因此老师决定调整队伍,......
  • 题解 CF1942F【Farmer John's Favorite Function】
    萌萌F题,上大分。首先,如下定义\(g(i)\):\(g(1)=\lfloor\sqrt{a_1}\rfloor\);对于所有\(i>1\),\(g(i)=\lfloor\sqrt{g(i-1)+a_i}\rfloor\)。也就是将\(f(i)\)的每一步运算后都向下取整。注意到\(\lfloorf(i)\rfloor=g(i)\)恒成立,于是我们只需要转而求每次修改后\(g(n......
  • P9870 [NOIP2023] 双序列拓展 题解
    题意:称某个序列\(B=\{b_1,b_2,\cdots,b_n\}\)是另一个序列\(A=\{a_1,a_2,\cdots,a_m\}\)的拓展当且仅当存在正整数序列\(L=\{l_1,l_2,\cdots,l_m\}\),将\(a_i\)替换为\(l_i\)个\(a_i\)后得到序列\(B\)。例如,\(\{1,3,3,3,2,2,2\}\)是\(\{1,3,3,2\}\)的拓展,......
  • CF1530G 题解
    考虑对操作进行转换。假设\(a_i\)为第\(i\)个\(1\)前面的\(0\)的个数。则操作可以进行如下转换:转换1:选择一个长度为\(k+1\)的子区间\(a_{l~l+k}\)。我们先把\(a_{l+1~l+k-1}\)翻转,然后更改\(a_l\)和\(a_{l+k}\)使得\(a_l+a_{l+k}\)不......
  • 【蓝桥杯选拔赛真题56】C++求位数 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编
    目录C++求位数一、题目要求1、编程实现2、输入输出二、算法分析三、程序编写四、程序说明五、运行结果六、考点分析七、推荐资料C++求位数第十四届蓝桥杯青少年创意编程大赛C++选拔赛真题一、题目要求1、编程实现给定一个正整数N(1<N<10^8),输出N为几位数2、......
  • CF1924D Balanced Subsequences 题解
    先判掉\(k>\min(n,m)\)的情况。首先有个明显的计算最长合法括号子序列的贪心方法:初始一个值为\(0\)的计数器,从左到右枚举每个字符,遇到(时将计数器加一,遇到)时如果计数器不为\(0\)就减一然后将答案加一。考虑绘制它的折线图。初始时纵坐标为\(0\),从左到右枚举每个字符......
  • 小猫爬山 C++题解
    小猫爬山内存限制:256MiB时间限制:1000ms标准输入输出题目类型:传统评测方式:文本比较题目描述Freda和rainbow饲养了N只小猫,这天,小猫们要去爬山。经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。Freda和rainbow只好花钱让它......
  • git clone失败问题解决
    gitclone失败问题解决背景当gitclone出现以下问题时:error:RPCfailed;curl92HTTP/2stream5wasnotclosedcleanly:CANCEL(err8)error:5492bytesofbodyarestillexpectedfetch-pack:unexpecteddisconnectwhilereadingsidebandpacketfatal:earlyE......
  • 小木棍 C++题解
    小木棍内存限制:1024MiB时间限制:1000ms标准输入输出题目类型:传统评测方式:文本比较题目描述乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每......