[题解][P6922][ICPC2016 WF]Longest Rivers
简要题意
有 \(n\) 条河和 \(m+1\) 个交汇点构成一棵以 \(0\) 号点(即大海) 为根的树。
每条河有各自的名称。对于一个交汇点,从它流出的干流的名称是流入这个交汇点的各个支流的名称之一。一条河流的长度是以它为名称的河流的长度之和。对于一个可能的命名方案,一条河流的排名等于长度大于它的河流数 \(+1\) 。
对于每条河,求出它在所有命名方案中,最小的排名。
\(1≤n≤500000\)。
解题思路
首先考虑如果单独求每条河流的答案怎么做,记当前河流为 \(p\),我们的目的是让它的排名尽可能的靠前。
显然,在从 \(p\) 到根的路径上,每个交汇点,我们都选择 \(p\)。
假设这样 \(p\) 最终长度为 \(L\),那么现在问题就是如何在其他交汇点进行决策,使得尽可能少的河流比 \(L\) 长。
如果一条河流比 \(L\) 长,我们称这条河流是长的,否则,称这条河流是短的,目标是最小化长河流的数目。
那么不难得到与 \(p\) 无关的交汇点的决策方式:
- 如果至少有一条河流是长的,让它继续流。
- 如果所有的河流都是短的,选择最短的让它继续流。
事实是,“在 \(p\) 到根的路径上总是选择 \(p\),最终长度为 \(L\),最小化比 \(L\) 长的河流”这个问题等价于“最小化比 \(L\) 长的河流”。
首先因为前者其实是多了关于 \(p\) 的限定,所以其答案不可能小于后者。
同时前者答案也不大于后者,假设后者最优方案中,\(p\) 长度小于 \(L\),在所有交汇点选择 \(p\),不会使答案增长。
故可以推出两个问题等价,那么我们考虑直接按照长度来求得答案。
对于已知的 \(L\),显然可以按照之前的决策来做,那么此时节点一共有三种可能的状态:
- 至少有一条将要进入该交汇点的河流是长的。
- 所有将要进入该交汇点的河流都是短的,但是流出该交汇点的河流将会变成长河流。
- 所有将要进入该交汇点的河流都是短的,并且流出该交汇点的河流也是短河流。
那么答案即为 \(2\) 状态的节点数目。
而随着 \(L\) 的增长,节点的状态变化是单调的,一定是 \(1\rightarrow 2,1\rightarrow 3,2\rightarrow 3\) 中的一种。
考虑决策被改变的情况,比如一条河流,原来被判定为长的,我们让其继续流,此时它变成短的了,我们选择让交汇点最短的河流继续流,也就是说,随着 \(L\) 的增长,每个交汇点流出的河流长度不增,那么就不难理解状态为什么这样变化。
\(L\) 的初值为 \(0\),此时只有叶子节点为 \(2\) 状态,其他节点都为 \(1\) 状态,那么一个节点什么时候才会发生状态变化呢 ?
对于 \(2\) 状态的节点,其子树内交汇点决策其实已经确定,故该节点流出的河流长度已经确定,只有当 \(L\) 增长的时候,其状态才可能变成 \(3\),而且我们显然可以求出其变为 \(3\) 状态的最小 \(L\) 是多少。
对于 \(1\) 状态的节点,不难发现,要当其儿子全部变为 \(3\) 状态时,其状态才会变。
代码
#include <bits/stdc++.h>
#define LL long long
#define lfor(i, x, y) for(int i(x), i##_end(y); i <= i##_end; ++i)
#define pb push_back
#define fi first
#define se second
#define endl '\n'
using namespace std;
const int N(500000 + 5);
const LL INF(1e18);
int n, m, fa[N * 2], w[N * 2];
int siz[N * 2], sum[N * 2]; LL len[N * 2], mx[N * 2];
string name[N];
vector <int> G[N];
map <LL, int> Ans;
priority_queue <pair<LL, int>> Q;
void dfs(int x){
if(x <= n) for(int y : G[x]) mx[y] = mx[x] + w[y], dfs(y);
}
signed main(){
ios :: sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
lfor(i, 1, n) cin >> name[i] >> fa[m + i] >> w[m + i];
lfor(i, 1, m) cin >> fa[i] >> w[i];
lfor(i, 1, n + m) ++siz[fa[i]], G[fa[i]].pb(i);
lfor(i, m + 1, n + m) Q.push({-w[i], i});
Ans[0] = Q.size();
while(!Q.empty()){
LL W = -Q.top().fi;
while(!Q.empty() && -Q.top().fi <= W){
int x = Q.top().se; Q.pop(); ++sum[fa[x]];
if(sum[fa[x]] == siz[fa[x]]){
len[fa[x]] = INF;
for(int y : G[fa[x]]) len[fa[x]] = min(len[fa[x]], len[y] + w[y]);
Q.push({-(len[fa[x]] + w[fa[x]]), fa[x]});
}
}
Ans[W] = Q.size();
}
Ans[INF] = 0;
dfs(0);
lfor(i, 1, n){
auto it = Ans.upper_bound(mx[m + i]); --it;
cout << name[i] << ' ' << it -> se + 1 << endl;
}
return 0;
}