BZOJ 4016: [FJOI2014]最短路径树问题

4016: [FJOI2014]最短路径树问题

Time Limit: 5 Sec   Memory Limit: 512 MB
Submit: 1602   Solved: 563
[ Submit][ Status][ Discuss]

Description

给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。

Input

第一行输入三个正整数n,m,K,表示有n个点m条边,要求的路径需要经过K个点。接下来输入m行,每行三个正整数Ai,Bi,Ci(1<=Ai,Bi<=n,1<=Ci<=10000),表示Ai和Bi间有一条长度为Ci的边。数据保证输入的是连通的无向图。

Output

输出一行两个整数,以一个空格隔开,第一个整数表示包含K个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。

Sample Input

6 6 4
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1

Sample Output

3 4



先建立出最短路径数,然后点分治可求。

f[i][0],表示当前深度为i的最大距离。

f[i][1],表示当前深度为i的最大距离数量。

最长路径和数量都可在分治过程中更新获得

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxm = 30005;
struct node 
{ 
	int v, len, next;
}edge[maxm * 4];
struct H 
{
	int d, id; 
}dis[maxm];
struct A
{
	int w, num;
}p[maxm], d[maxm];
vector<node>V[maxm];
int head[maxm * 4], vis[maxm], son[maxm], s[maxm], f[maxm][2], flag[maxm];
int root, cnt, n, m, ans, sn, M, L, rev, tot;
void add(int u, int v, int w)
{
	edge[cnt].v = v, edge[cnt].len = w, edge[cnt].next = head[u], head[u] = cnt++;
	edge[cnt].v = u, edge[cnt].len = w, edge[cnt].next = head[v], head[v] = cnt++;
}
void bfs()
{
	node temp;
	for (int i = 1;i <= n;i++) dis[i].d = 0x3f3f3f3f, dis[i].id = -1;
	queue<int>q;
	dis[1].d = 0, dis[1].id = 0;
	q.push(1);
	while (!q.empty())
	{
		int u = q.front();q.pop();
		for (int i = head[u];i != -1;i = edge[i].next)
		{
			int v = edge[i].v;
			if (dis[v].d > dis[u].d + edge[i].len || (dis[v].d == dis[u].d + edge[i].len&&dis[v].id > u))
			{
				V[v].clear();
				temp.v = u, temp.len = edge[i].len;
				V[v].push_back(temp);
				dis[v].d = dis[u].d + edge[i].len, dis[v].id = u;
				q.push(v);
			}
		}
	}
}
void work(int k)
{
	vis[k] = 1;
	for (int i = 0;i < V[k].size();i++)
	{
		add(k, V[k][i].v, V[k][i].len);
		if (vis[V[k][i].v]) continue;
		work(V[k][i].v);
	}
}
void getroot(int k, int pre)
{
	son[k] = 1, s[k] = 0;
	for (int i = head[k];i != -1;i = edge[i].next)
	{
		int v = edge[i].v;
		if (vis[v] || v == pre) continue;
		getroot(v, k);
		son[k] += son[v], s[k] = max(s[k], son[v]);
	}
	s[k] = max(s[k], sn - s[k]);
	if (s[root] > s[k]) root = k;
}
void getdep(int k, int pre)
{
	p[++tot] = d[k];
	for (int i = head[k];i != -1;i = edge[i].next)
	{
		int v = edge[i].v;
		if (v == pre || vis[v]) continue;
		d[v].w = d[k].w + edge[i].len, d[v].num = d[k].num + 1;
		getdep(v, k);
	}
}
void query(int k,int w,int num)
{
	d[k].w = w, d[k].num = num;
	tot = 0, getdep(k, 0);
	for (int i = 1;i <= tot;i++)
	{
		if (p[i].num < M)
		{
			int len = p[i].w + f[M - p[i].num][0];
			if (len > L) L = len, ans = f[M - p[i].num][1];
			else if (len == L) ans += f[M - p[i].num][1];
		}
		else if (p[i].num == M)
		{
			if (p[i].w > L) L = p[i].w, ans = 1;
			else if (p[i].w == L) ans++;
		}
	}
	for (int i = 1;i <= tot;i++)
	{
		if (p[i].num <= M)
		{
			if (p[i].w > f[p[i].num][0]) f[p[i].num][0] = p[i].w, f[p[i].num][1] = 1;
			else if (p[i].w == f[p[i].num][0]) f[p[i].num][1]++;
			flag[++rev] = p[i].num;
		}
	}
}
void dfs(int k)
{
	vis[k] = 1, rev = 0;
	for (int i = head[k];i != -1;i = edge[i].next)
	{
		int v = edge[i].v;
		if (vis[v]) continue;
		query(v, edge[i].len, 1);
	}
	for (int i = 1;i <= rev;i++) f[flag[i]][1] = f[flag[i]][0] = 0;
	for (int i = head[k];i != -1;i = edge[i].next)
	{
		int v = edge[i].v;
		if (vis[v]) continue;
		root = 0, sn = son[v];
		getroot(v, 0), dfs(root);
	}
}
int main()
{
	int i, j, k, sum, x, y, z;
	scanf("%d%d%d", &n, &m, &M);
	memset(head, -1, sizeof(head)), cnt = 0, M--;
	for (i = 1;i <= m;i++)
	{
		scanf("%d%d%d", &x, &y, &z);
		add(x, y, z);
	}
	bfs();
	memset(head, -1, sizeof(head)), cnt = 0;
	memset(vis, 0, sizeof(vis));
	for (i = 1;i <= n;i++)
		if (!vis[i]) work(i);
	memset(vis, 0, sizeof(vis));
	L = 0, ans = 0;
	root = 0, s[0] = 100000000, sn = n;
	getroot(1, 0), dfs(root);
	printf("%d %d\n", L, ans);
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值