首页 > 其他分享 >P6922 [ICPC2016 WF] Longest Rivers 题解

P6922 [ICPC2016 WF] Longest Rivers 题解

时间:2023-12-25 15:56:48浏览次数:37  
标签:ICPC2016 std WF int 题解 流过来 kMaxN fa 号点

Description

有 \(n\) 条河和 \(m+1\) 个交汇处构成一棵以 \(0\) 号点(即大海) 为根的树。

每条河有各自的名称。对于一个交汇处,从它流出的干流的名称是流入这个交汇处的各个支流的名称之一。一条河流的长度是以它为名称的河流的长度之和。对于一个可能的命名方案,一条河流的排名等于长度大于它的河流数 \(+1\) 。

对于每条河,求出它在所有命名方案中,最小的排名。

\(n,m\leq 5\times 10^5\)。

Solution

先考虑对于每条河怎么 \(O(n)\) 求答案。

容易发现当前河一定要一冲到底,设长度为 \(L\),那么就要要求其余长度 \(>L\) 的河流数量最少。

然后从下往上考虑每个点,有下面 2 种情况:

  1. 如果有儿子流过来是长的,那么当前点就流这个最长的。
  2. 如果儿子流过来全是短的,那么流里面最短的。

这样递归处理即可做到 \(O(n)\)。


考虑怎样一次求出所有的答案。

这里有个转化就是只要求出所有河流中 \(>L\) 的河流的最小数量,而不用考虑当前河是否要连到根。

因为如果一个最小数量的方案当前河没有连到根,那么把当前河到根的路径上全都连到当前河一定不会更劣。

然后从小到大枚举 \(L\),这时候有 3 种情况:

  1. 有儿子流过来是长的。
  2. 儿子流过来全是短的,但最短的流完当前点到父亲的边后变长。
  3. 儿子流过来全是短的,但最短的流完当前点到父亲的边后还是短的。

容易发现答案就是 2 号点的数量\(+1\),并且随着 \(L\) 的增大,部分 2 号点会变为 3 号点,1 号点可能会变成 2 或 3 号点。

设 \(f_i\) 表示将 \(i\) 从 2 变到 3 的最小长度。

那么只要用一个 pair 的优先队列维护所有 2 号点,其中 \(\{l,x\}\) 分别表示 \(x\) 需要从 2 变到 3 的最小长度和当前点的编号。

每次从队列中取出 \(l\) 最小的 \(x\),然后不停跳父亲,如果这个祖先 \(y\) 的所有儿子都变 3 并且 \(f_y\leq L\) 说明 \(y\) 也是 3 号点,就继续跳,否则退出。

最后如果跳出来一个 \(y\),使得他的所有儿子全是 3 并且 \(f_y>L\) 就说明他变成了个 2 号点,加到队列里即可。

如果要输出一个任意 \(L\) 的答案,就只要找到求出的所有答案中小于等于 \(L\) 的最大长度的答案即可。

时间复杂度:\(O(n\log n)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e6 + 5;
const int64_t kInf = 1e18;

int n, m;
int fa[kMaxN], w[kMaxN], cnt[kMaxN];
int64_t sum[kMaxN], f[kMaxN];
std::string name[kMaxN];
std::vector<int> G[kMaxN];
std::map<int64_t, int> mp;
std::priority_queue<std::pair<int64_t, int>, std::vector<std::pair<int64_t, int>>, std::greater<std::pair<int64_t, int>>> q;

void dfs(int u) {
  int64_t minn = kInf;
  for (auto v : G[u]) {
    sum[v] = sum[u] + w[v];
    dfs(v);
    minn = std::min(minn, f[v]);
    ++cnt[u];
  }
  if (minn == kInf) f[u] = w[u];
  else f[u] = minn + w[u];
}

void dickdreamer() {
  std::cin >> n >> m;
  for (int i = m + 1; i <= m + n; ++i) {
    std::cin >> name[i] >> fa[i] >> w[i];
    G[fa[i]].emplace_back(i);
  }
  for (int i = 1; i <= m; ++i) {
    std::cin >> fa[i] >> w[i];
    G[fa[i]].emplace_back(i);
  }
  dfs(0);
  for (int i = m + 1; i <= m + n; ++i)
    q.emplace(f[i], i);
  for (; !q.empty();) {
    auto [L, u] = q.top();
    q.pop();
    --cnt[fa[u]];
    for (u = fa[u]; u && !cnt[u]; u = fa[u]) {
      if (L >= f[u]) {
        --cnt[fa[u]];
      } else {
        q.emplace(f[u], u);
        break;
      }
    }
    mp[L] = q.size() + 1;
  }
  mp[kInf] = 0;
  for (int i = m + 1; i <= m + n; ++i)
    std::cout << name[i] << ' ' << prev(mp.upper_bound(sum[i]))->second << '\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;
}

标签:ICPC2016,std,WF,int,题解,流过来,kMaxN,fa,号点
From: https://www.cnblogs.com/Scarab/p/17926255.html

相关文章

  • SOJ1972 题解
    题意设\(S\)为一个可重数集,满足所有元素均为非负整数。你可以对\(S\)进行若干次(可以为\(0\)次)如下操作:选择一个非负整数\(x\)满足\(x\)至少在\(S\)中出现了\(2\)次,在\(S\)中删除一个\(x\),并将\((x-1)\)或\((x+1)\)插入\(S\)。如果你选择插入\((x-1)\),你必......
  • VS2022远程调试Linux程序卡住问题解决
    问题:说明:使用vs2022第一次远程调试linux上的程序时,会出现调试器启动时卡住问题。原因就是第一次调试时,会在目标服务器下下载vsdbg工具,因为下载源在国外,所以下载特别慢,就会造成卡住的现象。解决:uname-m 查看远程调试时,用户文件夹下会多一个.vs-debugger隐藏文件夹,如果是使用......
  • 【已解决-实操篇】SaTokenException: 非Web上下文无法获取Request问题解决-实操篇
    在上一篇《【理论篇】SaTokenException:非Web上下文无法获取Request问题解决-理论篇》中,凯哥(凯哥Java)介绍了产生这个问题的源码在哪里,以及怎么解决的方案。没有给出实际操作步骤。本文,凯哥就通过threadLocal方案来解决。一、创建用于存放共享变量的对象代码如下:packagecom.kai......
  • [SDOI2010] 大陆争霸 题解
    [题目传送门](https://www.luogu.com.cn/problem/P2446)#解法由题可知,一个城市$u$保护城市$v$,所以建一条边$u\tov$表示城市$u$保护城市$v$,因为题目说保证有解,所以建的图一定是一个**有向无环图$DAG$**。再在此基础上求出最短路径。具体过程为设$dis_u$表示实际到达(攻破)$u$的最......
  • 「HCOI-R1」报名人数 题解
    博客园。我们会发现,\(2\)和\(3\)的火柴个数是一样的,\(9\)和\(0\)的火柴个数是一样的。所以只有在\(12\)到\(13\)这样是合法的,自己推一下可以知道,最多只有连续两个。而在\(l\)到\(r\)的长度大于\(9\)的时候可以直接输出\(2\)。剩下的情况直接暴力枚举即可。#......
  • CF contest 1909 Pinely Round 3 (Div. 1 + Div. 2) 题解(Vanilla的掉分赛)
    CFcontest1909PinelyRound3(Div.1+Div.2)Vanilla的掉分赛绪言PinelyRound3(Div.1+Div.2)-Codeforces\[\color{purple}\large\textbf{世界上只有一种真正的英雄主义,}\]\[\color{red}\large\textbf{就是认清了生活的真相后还依然热爱它。}\]\[\color{gray}......
  • AtCoder Beginner Contest 334题解
    ⭐AtCoderBeginnerContest334前言:比赛题目链接--按照惯例只写了主要部分的代码--特别说明:Rust有一个竞赛用的输入库,并且写ABC是可以用的,但是平时写洛谷是用不了的,所以我自己写了一个cin(),凑活能用,代码见下:读输入函数fncin()->String{letmutinput=String......
  • 【题解】洛谷P1496 火烧赤壁 (离散化)
    P1496火烧赤壁-洛谷|计算机科学教育新生态(luogu.com.cn)我们首先先看数据,n<=20000,数据不多,但是范围大(-10^9<=Ai,Bi<=10^9),这时,就可以用离散化了。但是在这里我们会遇到区间重合的问题(也可以使用区间合并),如下图本题的题意是让我们求出燃烧位置的长度之和。区间重合时只......
  • [ABC265F] Manhattan Cafe 题解
    [ABC265F]ManhattanCafe题解思路解析很有思维难度的一道题。思路是dp,\(f[i][j][k]\)表示已经计算了\(i\)维,距离点\(p\)的距离为\(j\),距离点\(q\)的距离为\(k\)时的整点\(r\)个数,由此可见我们的每一维都可以从上一维推出来,也即\(f[i][j][k]\)可以由\(f[i-1][j......
  • ABC334 全套题解
    A-ChristmasPresent简单题。voidslv(){ inta=Read<int>(),b=Read<int>(); if(a>b)Puts("Bat"); elsePuts("Glove"); return;}B-ChristmasTrees也是简单题。constexpri128INF=-1e18;i128a,m,l,r;voidslv(......