洛谷 P6922 [ICPC2016 WF]Longest Rivers 题解
一、题目:
洛谷原题
codeforces原题
二、思路:
这道题官方题解讲的非常好,我这里就当是简单的翻译一下官方的题解吧。
让我们从一条单独的河流
如果一条河流比
- 如果至少有一条将要进入该汇流点的河流是长的,那么我们就可以让它继续流(从而终止其他河流)。
- 如果所有的河流都是短的,那么我们要选择最短的河流让它继续流。
时间复杂度
现在,让我们致力于一次性计算出所有河流的答案。
对于每个河流
所以我们需要对于每个
假设当前处理到了长度
- 至少有一条将要进入该交汇点的河流是长的(叶子结点不可能处于这种状态)。
- 所有将要进入该交汇点的河流都是短的,但是流出该交汇点的河流将会变成长河流。
- 所有将要进入该交汇点的河流都是短的,并且流出该交汇点的河流也是短河流。
在第二种和第三种状态,根据贪心原则,我们选择最短的河流让它流出去。
让我们以
当
当
我们对于所有是状态 2 的节点维护一个小根堆,堆中保存的是二元组
三、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef pair<long long, int> PLI;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int MAXN = 1000005;
const long long INF = 1e17;
int n, m, head[MAXN], tot;
int v[MAXN], fa[MAXN], cnt[MAXN];
long long dp[MAXN], sum[MAXN];
string name[MAXN];
map<long long, int>ans;
map<long long, int>::iterator it;
struct Edge {
int y, next;
Edge() {}
Edge(int _y, int _next) : y(_y), next(_next) {}
}e[MAXN];
priority_queue<PLI, vector<PLI>, greater<PLI> >q;
inline void connect(int x, int y) {
e[++tot] = Edge(y, head[x]);
head[x] = tot;
}
void dfs(int x, int father) {
fa[x] = father;
sum[x] = sum[father] + v[x];
long long minn = INF;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].y;
dfs(y, x);
++cnt[x];
minn = min(minn, dp[y]);
}
if (minn == INF) dp[x] = v[x];
else dp[x] = minn + v[x];
}
int main() {
n = read(); m = read();
for (int i = m + 1; i <= m + n; ++i) {
cin >> name[i];
int y = read(); v[i] = read();
connect(y, i);
}
for (int i = 1; i <= m; ++i) {
int y = read();
v[i] = read();
connect(y, i);
}
dfs(0, 0);
int now = 0;
long long L = 0;
for (int i = m + 1; i <= m + n; ++i) {
q.push({ dp[i], i });
++now; // 维护状态2的节点个数。
}
while (q.size()) {
int x = q.top().second; long long l = q.top().first; q.pop();
L = l;
--now;
--cnt[fa[x]];
if (fa[x] && !cnt[fa[x]]) {
x = fa[x];
while (x && L >= dp[x]) { // 检查x的祖先们。
--cnt[fa[x]];
if (cnt[fa[x]] == 0) {
x = fa[x];
}
else break;
}
if (x && cnt[x] == 0 && L < dp[x]) {
q.push({ dp[x], x });
++now;
}
}
ans[L] = now + 1;
}
ans[INF] = 0; // 防止越界。
for (int i = m + 1; i <= n + m; ++i) {
it = ans.upper_bound(sum[i]);
--it; // 由于在求解的过程中,L的增长并不是连续的,所以我们要找最后一个小于等于sum[i]的L所对应的答案。
cout << name[i] << " " << (*it).second << endl;
}
return 0;
}