洛谷·[FJOI2014]最短路径树问题

初见安~这里是传送门:洛谷P2993 最短路径树问题

题目描述

给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。

往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。

可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?

这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。

输入格式

第一行输入三个正整数n,m,K,表示有n个点m条边,要求的路径需要经过K个点。

接下来输入m行,每行三个正整数Ai,Bi,Ci(1<=Ai,Bi<=n,1<=Ci<=10000),表示Ai和Bi间有一条长度为Ci的边。

数据保证输入的是连通的无向图。

输出格式

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

输入 

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

输出 

3 4

说明/提示

对于所有数据n<=30000,m<=60000,2<=K<=n。

数据保证最短路径树上至少存在一条长度为K的路径。

题解

题意好像有点儿争议……我再复述一遍:给你一张图,求出一棵最短路径树,并且问你这棵树上包含K个点的最长路径的长度以及在这棵树上以此为长度的路径有多少条。也就是说我们把树求出来了,原图就可以扔了。【迷惑。求法上文附连接。

接下来我们考虑包含K个点的最长路径——也就是包含K-1条边的最长路径以及次数。求最长路径可能有点儿麻烦,但是次数是很好求的——求一棵树上长度为X的路径的出现次数这不就是点分治的板子题嘛!!!那长度呢?我们动态长度吧【大雾】意思就是我们边求数量边更新长度, 如果相同那就累加,更长就更新。

就这样。是个点分治的模板变式。

上代码——

#include<algorithm>//所求:1.包含K个点的最长路径条数 2. 该长度的路径条数 最后的路径数量可能要 >>1 。A->B = B->A 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
#define maxn 30005
#define maxm 60005
using namespace std;
typedef long long ll;
int read() {
	int x = 0, ch = getchar();
	while(!isdigit(ch)) ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x;
}

//基础设定 
int n, m, K;
struct edge {
	int to, w, nxt;
	edge() {}
	edge(int tt, int ww, int nn) {to = tt, w = ww, nxt = nn;}
}e[maxn << 1], e_[maxm << 1];

int head[maxn], k = 0;
void add(int u, int v, int w) {e[k] = edge(v, w, head[u]); head[u] = k++;}

int head_[maxn], k_ = 0;
void add_(int u, int v, int w) {e_[k_] = edge(v, w, head_[u]); head_[u] = k_++;}

//最短路径树 
int dis[maxn], fa[maxn], eg[maxn];
priority_queue<pair<int, int> > q;
void dij(edge *e, int *head) {//大致为最短路的板子
	memset(dis, 0x3f, sizeof dis); dis[1] = 0;
	memset(fa, 0x3f, sizeof fa); fa[1] = 0;
	q.push(make_pair(0, 1));
	while(q.size()) {
		register int u = q.top().second; q.pop();
		for(int i = head[u]; ~i; i = e[i].nxt) {
			register int v = e[i].to;
			if(dis[u] + e[i].w < dis[v]) {
				dis[v] = dis[u] + e[i].w; fa[v] = u; eg[v] = e[i].w; 
                q.push(make_pair(-dis[v], v));
			}
			else if(dis[u] + e[i].w == dis[v] && u < fa[v]) {//fa记录树上的父节点
				fa[v] = u; eg[v] = e[i].w; q.push(make_pair(-dis[v], v));
			}
		}
	}
}

//点分治
int size[maxn], root, part[maxn], max_num, lenth = 0;
bool vis[maxn];
void get_root(int u, int fa) {
	size[u] = 1; part[u] = 0;
	for(int i = head[u]; ~i; i = e[i].nxt) {
		register int v = e[i].to; if(v == fa || vis[v]) continue;
		get_root(v, u); size[u] += size[v];
		part[u] = max(part[u], size[v]);
	}
	part[u] = max(part[u], max_num - size[u]);
	if(part[u] < part[root]) root = u;
}

struct node {
	int pointnum, lenth;
	node() {}
	node(int pp, int ll) {pointnum = pp, lenth = ll;}
}judge[maxm], ans, len[maxm], d[maxn];


//len和d数组的pointnum是边数……【不能用点数QuQ
void get_dis(int u, int fa) {
	if(d[u].pointnum <= K) len[++len[0].pointnum] = d[u];
	for(int i = head[u]; ~i; i = e[i].nxt) {
		register int v = e[i].to; if(v == fa || vis[v]) continue;
		d[v].pointnum = d[u].pointnum + 1; d[v].lenth = d[u].lenth + e[i].w;
		get_dis(v, u);
	}
}

//judge和ans的pointment是数量统计,lenth是记录的最长长度
void cal(int u) {
	len[0].pointnum = 0; get_dis(u, 0);
	for(int i = 1; i <= len[0].pointnum; i++) {//和桶judge匹配
		register int num = len[i].pointnum, lth = len[i].lenth;
		if(num < K - 1) {//需要匹配
			register int tmp = K - 1 - num;
			if(judge[tmp].lenth + lth > ans.lenth) 
                ans = node(judge[tmp].pointnum, judge[tmp].lenth + lth);
			else if(judge[tmp].lenth + lth == ans.lenth) 
                ans.pointnum += judge[tmp].pointnum;
		}
		else if(num == K - 1) {//自成一路
			if(lth > ans.lenth) ans = node(1, lth);
			else if(lth == ans.lenth) ans.pointnum++;
		}
	}
	
	for(int i = 1; i <= len[0].pointnum; i++) {//累加进桶
		register int num = len[i].pointnum, lth = len[i].lenth;
		if(num < K - 1) {
			if(lth > judge[num].lenth) judge[num] = node(1, lth);
			else if(lth == judge[num].lenth) judge[num].pointnum++;
		}
	}
}
    
void solve(int u) {
	vis[u] = true; dis[u] = 1;	
	for(int i = head[u]; ~i; i = e[i].nxt) {
		register int v = e[i].to; if(vis[v]) continue;
		d[v].pointnum = 1, d[v].lenth = e[i].w; 
		cal(v);//处理各个子树
	}
	
	for(int i = 0; i <= K * 2; i++) judge[i].pointnum = judge[i].lenth = 0;//记得清空

	for(int i = head[u]; ~i; i = e[i].nxt) {
		register int v = e[i].to; if(vis[v]) continue;
		max_num = size[v]; root = 0; get_root(v, u);
		solve(root);
	}
}

signed main() {
	memset(head, -1, sizeof head);
	memset(head_, -1, sizeof head_);
	n = read(), m = read(), K = read();
	for(int u, v, w, i = 1; i <= m; i++) u = read(), v = read(), w = read(), add_(u, v, w), add_(v, u, w);
	dij(e_, head_);//因为原图后期不需要,所以加个下划线。
	
	for(int i = 2; i <= n; i++) add(i, fa[i], eg[i]), add(fa[i], i, eg[i]);//存树

	max_num = part[0] = n; root = 0;
	get_root(1, 0);
	solve(root);
	printf("%d %d\n", ans.lenth, ans.pointnum);
	return 0;
}

这个题思路比较简单,写起来还是……比较有意思吧。

迎评:)
——End——

 

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值