2016 World Final F Longest Rivers

题意:

有n个河流@#¥……()……)——

思路:


考虑河流i,让他尽量排在最前的话,肯定是他直接走到根,对于其他的叶子节点,如果他比河流i短的话,他可以向上覆盖一定的边。所以,还有一些边是没有被覆盖的, 对这些边做一颗生成树,那么比这个河流长的河流个数就是1 + sum {deg[i] - 1}。因此,可以按照dep从大到小来考虑每一个河流,同时,一些边会在某些时刻加到生成树里面,维护1 +sum { deg[i] - 1 }就行了。

#include <bits/stdc++.h>

using namespace std;

#define LL long long
#define pii pair<int, int>
#define MP make_pair
#define mod 1000000007
#define eps 1e-12
#define Pi acos(-1.0)
#define N 1000020
#define M 2000020
#define PB push_back
#define MP make_pair
#define fi first
#define se second

const LL inf = 1LL << 60;

int fst[N], nxt[M], vv[M], cost[M], e;

void init() {
	memset(fst, -1, sizeof fst);
	e = 0;
}
void add(int u, int v, int c) {
	vv[e] = v, cost[e] = c, nxt[e] = fst[u], fst[u] = e++;
}

int n, m, fa[N], bt[N];
LL dis[N], midis[N];
pair<LL, int> id[N];
vector<int> g[N];
bool vis[N];
string name[N];
char s[100];
int ans[N];
int tot;
int deg[N];

void dfs(int u, int p) {
	fa[u] = p;
	midis[u] = inf;
	for(int i = fst[u]; ~i; i = nxt[i]) {
		int v = vv[i];
		dis[v] = dis[u] + cost[i];
		bt[v] = cost[i];
		dfs(v, u);
		midis[u] = min(midis[u], cost[i] + midis[v]);
	}
	if(midis[u] == inf) midis[u] = 0;
}
void add_it(int u) {
	if(vis[u]) return;
	vis[u] = 1;
	int p = fa[u];
	deg[p]++;
	if(tot == 0) tot++;
	else
		if(deg[p] > 1) tot++;
	add_it(p);
}
int main() {
	init();
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) {
		int p, c;
		scanf("%s%d%d", s, &p, &c);
		add(p + 1 + n, i, c);
		name[i] = s;
	}
	for(int i = 1; i <= m; ++i) {
		int p, c;
		scanf("%d%d", &p, &c);
		add(p + 1 + n, i + n + 1, c);
	}
	dfs(n + 1, 0);
	for(int i = 1; i <= n; ++i) id[i] = MP(dis[i], i);
	sort(id + 1, id + n + 1);

	for(int i = 1; i <= n + m + 1; ++i) {
		if(i == n + 1) continue;
		LL t = midis[i] + bt[i];
		int p = lower_bound(id + 1, id + n + 1, MP(t, -1)) - id;
		g[p].PB(i);
		if(p > n) {
			vector<int> gg;
			gg[1] = -1;
		}
	}
	tot = 0;
	vis[n + 1] = 1;
	for(int i = n; i >= 1; --i) {
		int u = id[i].se;
		ans[u] = tot;
		for(auto v: g[i]) {
			add_it(v);
		}
	}
	for(int i = 1; i <= n; ++i) printf("%s %d\n", name[i].c_str(), ans[i] + 1);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值