Loading

[题解][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;
}
posted @ 2022-08-02 09:17  IrisT  阅读(67)  评论(0编辑  收藏  举报