HDU5016-Mart Master II

Mart Master II

                                                                 Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
                                                                                             Total Submission(s): 836    Accepted Submission(s): 306


Problem Description
Trader Dogy lives in city S, which consists of n districts. There are n - 1 bidirectional roads in city S, each connects a pair of districts. Indeed, city S is connected, i.e. people can travel between every pair of districts by roads.

In some districts there are marts founded by Dogy’s competitors. when people go to marts, they’ll choose the nearest one. In cases there are more than one nearest marts, they’ll choose the one with minimal city number.

Dogy’s money could support him to build only  one new marts, he wants to attract as many people as possible, that is, to build his marts in some way that maximize the number of people who will choose his mart as favorite. Could you help him?
 

Input
There are multiple test cases. Please process till EOF.

In each test case: 

First line: an integer n indicating the number of districts.

Next n - 1 lines: each contains three numbers b i, e i and w i, (1 ≤ b i,e i ≤ n,1 ≤ w i ≤ 10000), indicates that there’s one road connecting city b i and e i, and its length is w i.

Last line : n(1 ≤ n ≤ 10 5) numbers, each number is either 0 or 1, i-th number is 1 indicates that the i-th district has mart in the beginning and vice versa.
 

Output
For each test case, output one number, denotes the number of people you can attract, taking district as a unit.
 

Sample Input
  
  
5 1 2 1 2 3 1 3 4 1 4 5 1 1 0 0 0 1 5 1 2 1 2 3 1 3 4 1 4 5 1 1 0 0 0 0 1 1 1 0
 

Sample Output
  
  
2 4 0 1
 

Source
 

Recommend
hujie
 

题意:给定一个n个点的图,这个图是一棵树,然后有些点建立了集市。并且没有集市的地方去集市一定是去最近的,如果距离相同,那么则去标号小的。现在再建一个集市,问建完这个集市最多有多少个点来这里

解题思路:树分治,先求出初始状态离每个点最近的关键点是哪个,然后再考虑用点分治求每个点作为关键点能覆盖的点的数量,当以root为根时如果u能覆盖v,那么应该有d[u] + d[v] < dis[v],其中dis[v]是事前预处理出来离v最近的关键点距离,也就是dis[u] < now[v] - dis[v],所以我们就可以把以root为根的这棵子树的所有点的dis都一遍dfs出来,然后枚举u来更新答案,注意这时在同一棵子树下的u和v会在点分治的过程中被多次重复计算,所以之后还要再减掉


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cctype>
#include <map>
#include <cmath>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>

using namespace std;

#define LL long long
const int INF = 0x3f3f3f3f;

const int maxn = 2e5 + 10;
int n, m, x, y, w;
int s[maxn], nt[maxn], e[maxn], val[maxn], cnt;
int sum[maxn], mx[maxn], dis[maxn], vis[maxn];
int id[maxn], flag[maxn], ans[maxn], tot;

struct node
{
	int id, dis, flag;
	node(int id = 0, int dis = 0, int flag = 0) :id(id), dis(dis), flag(flag) {}
	bool operator<(const node &a)const
	{
		if (dis != a.dis) return dis > a.dis;
		else return flag > a.flag;
	}
}pre;
pair<int, int>d[maxn], D[maxn];

void Dijkstra()
{
	priority_queue<node>q;
	memset(vis, 0, sizeof vis);
	memset(dis, INF, sizeof dis);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &flag[i]);
		if (flag[i]) q.push(node(i, dis[i] = 0, id[i] = i));
	}
	while (!q.empty())
	{
		pre = q.top();
		q.pop();
		vis[pre.id] = 1;
		for (int i = s[pre.id]; ~i; i = nt[i])
		{
			if (vis[e[i]]) continue;
			if (dis[e[i]] > pre.dis + val[i])
			{
				dis[e[i]] = pre.dis + val[i];
				id[e[i]] = pre.flag;
				q.push(node(e[i], dis[e[i]], id[e[i]]));
			}
			else if (dis[e[i]] == pre.dis + val[i] && id[e[i]] > pre.flag)
			{
				id[e[i]] = pre.flag;
				q.push(node(e[i], dis[e[i]], id[e[i]]));
			}
		}
	}
	memset(vis, 0, sizeof vis);
}

int dfs(int k, int fa, int p)
{
	int ans = 0;
	sum[k] = (mx[k] = 0) + 1;
	for (int i = s[k]; ~i; i = nt[i])
	{
		if (e[i] == fa || vis[e[i]]) continue;
		int temp = dfs(e[i], k, p);
		sum[k] += sum[e[i]];
		mx[k] = max(mx[k], sum[e[i]]);
		if (mx[temp] < mx[ans]) ans = temp;
	}
	mx[k] = max(mx[k], p - sum[k]);
	return mx[k] < mx[ans] ? k : ans;
}

void get(int k, int fa, int len)
{
	d[tot++] = make_pair(k, len);
	for (int i = s[k]; ~i; i = nt[i])
	{
		if (vis[e[i]] || fa == e[i]) continue;
		get(e[i], k, len + val[i]);
	}
}

void Find(int k, int p, int len)
{
	tot = 0;
	get(k, k, len);
	for (int i = 0; i < tot; i++) D[i] = make_pair(dis[d[i].first] - d[i].second, id[d[i].first]);
	sort(D, D + tot);
	for (int i = 0; i < tot; i++)
	{
		int k = lower_bound(D, D + tot, make_pair(d[i].second, d[i].first)) - D;
		ans[d[i].first] += p*(tot - k);
	}
}

void build(int k, int p)
{
	int y = dfs(k, k, p);
	Find(y, 1, 0), vis[y] = 1;
	for (int i = s[y]; ~i; i = nt[i])
	{
		if (vis[e[i]]) continue;
		Find(e[i], -1, val[i]);
		if (sum[e[i]] < sum[y]) build(e[i], sum[e[i]]);
		else build(e[i], p - sum[y]);
	}
}

int main()
{
	while (~scanf("%d", &n))
	{
		memset(s, -1, sizeof s);
		memset(ans, 0, sizeof ans);
		mx[cnt = 0] = INF;
		for (int i = 1; i < n; i++)
		{
			scanf("%d%d%d", &x, &y, &w);
			nt[cnt] = s[x], s[x] = cnt, e[cnt] = y, val[cnt++] = w;
			nt[cnt] = s[y], s[y] = cnt, e[cnt] = x, val[cnt++] = w;
		}
		Dijkstra();
		build(1, n);
		int res = 0;
		for (int i = 1; i <= n; i++)
			if (!flag[i]) res = max(ans[i], res);
		printf("%d\n", res);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值