【BZOJ4016】[FJOI2014]最短路径树问题 最短路径树+点分治

【BZOJ4016】[FJOI2014]最短路径树问题

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

HINT

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

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

2016.12.7新加数据一组by - wyxxqz-150137

题解:做这种题总有一种奇怪的违和感,感觉就是强行把两道题拼起来变成一道题考~

子任务1:求最短路径树,这个直接Dijkstra+DFS就好,DFS时先走编号小的点。

子任务2:求树上包含k个点的最长路径的长度及条数,这个显然点分治。在以x为分治中心时,我们依次遍历它的所有儿子的子树,用fl[i]表示在之前的子树中,包含i个点的链的最长路径长度,用fs[i]表示条数;用gl[i]表示在当前的子树中,包含i个点的链的最长路径长度,用gs[i]表示条数,然后搞一搞就行了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <utility>
#include <vector>
#include <algorithm>
#define mp(A,B)	make_pair(A,B)
#define fir(_) ((_).first)
#define sec(_) ((_).second)
using namespace std;
typedef pair<int,int> pii;
const int maxn=30010;
const int inf=0x3f3f3f3f;
int n,m,K,cnt,root,tot,maxx,ans,sum,d;
int to[maxn<<1],next[maxn<<1],val[maxn<<1],head[maxn],dis[maxn],vis[maxn],dep[maxn];
int fl[maxn],fs[maxn],gl[maxn],gs[maxn],siz[maxn];
vector<pii> e[maxn];
priority_queue<pii> pq;
void add(int a,int b,int c)
{
	//printf("%d %d %d\n",a,b,c);
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void dijkstra()
{
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0;
	int i,u;
	pii y;
	pq.push(mp(0,-1));
	while(!pq.empty())
	{
		u=-sec(pq.top()),pq.pop();
		if(vis[u])	continue ;
		vis[u]=1;
		for(int i=0;i<e[u].size();i++)
		{
			y=e[u][i];
			if(dis[fir(y)]>dis[u]+sec(y))
				dis[fir(y)]=dis[u]+sec(y),pq.push(mp(-dis[fir(y)],-fir(y)));
		}
	}
	memset(vis,0,sizeof(vis));
}
void Dfs(int x)
{
	vis[x]=1;
	for(int i=0;i<e[x].size();i++)
	{
		pii y=e[x][i];
		if(!vis[fir(y)]&&dis[fir(y)]==dis[x]+sec(y))
			Dfs(fir(y)),add(x,fir(y),sec(y)),add(fir(y),x,sec(y));
	}
}
void getr(int x,int fa)
{
	siz[x]=1;
	int i,mx=0;
	for(i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]]||to[i]==fa)	continue;
		getr(to[i],x),siz[x]+=siz[to[i]];
		mx=max(mx,siz[to[i]]);
	}
	if(maxx>max(tot-siz[x],mx))	maxx=max(tot-siz[x],mx),root=x;
}
void getd(int x,int fa,int dep,int len)
{
	if(dep>=K)	return ;
	d=max(d,dep);
	if(gl[dep]<len)	gl[dep]=len,gs[dep]=0;
	if(gl[dep]==len)	gs[dep]++;
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]]||to[i]==fa)	continue;
		getd(to[i],x,dep+1,len+val[i]);
	}
}
void dfs(int x)
{
	vis[x]=1;
	int i,j,dd=0;
	fs[0]=1;
	for(i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]])	continue;
		d=0,getd(to[i],x,1,val[i]),dd=max(dd,d);
		for(j=1;j<=d;j++)
		{
			if(ans<fl[K-j-1]+gl[j])	ans=fl[K-j-1]+gl[j],sum=0;
			if(ans==fl[K-j-1]+gl[j])	sum+=fs[K-j-1]*gs[j];
		}
		for(j=1;j<=d;j++)
		{
			if(fl[j]<gl[j])	fl[j]=gl[j],fs[j]=0;
			if(fl[j]==gl[j])	fs[j]+=gs[j];
			gl[j]=-inf,gs[j]=0;
		}
	}
	for(i=1;i<=dd;i++)	fl[i]=-inf,fs[i]=0;
	for(i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]])	continue;
		tot=siz[to[i]],maxx=1<<30,getr(to[i],x),dfs(root);
	}
}
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	//freopen("bz4016.in","r",stdin);
	//freopen("bz4016.out","w",stdout);
	n=rd(),m=rd(),K=rd();
	int i,a,b,c;
	for(i=1;i<=m;i++)	a=rd(),b=rd(),c=rd(),e[a].push_back(mp(b,c)),e[b].push_back(mp(a,c));
	for(i=1;i<=n;i++)	sort(e[i].begin(),e[i].end());
	memset(head,-1,sizeof(head));
	dijkstra(),Dfs(1);
	memset(vis,0,sizeof(vis));
	for(i=1;i<=n;i++)	fl[i]=gl[i]=-inf;
	tot=n,maxx=1<<30,getr(1,0),dfs(root);
	printf("%d %d",ans,sum);
	return 0;
}
时间: 2024-10-10 17:37:49

【BZOJ4016】[FJOI2014]最短路径树问题 最短路径树+点分治的相关文章

“中兴捧月”比赛之——二叉查找树(BST)树的最短路径Java求解

问题描述: BST树,又称二叉查找树,求其到所有叶子节点路径的最小值 测试用例一:  10 5 20 返回15: 测试用例二: 100 20 70 110 120 10 null null 89 null null null null 返回130: 程序代码实现: 1 package examination.written; 2 3 /** 4 * 5 * @author ZhuXY 6 * @time 2016-6-12 下午9:57:53 7 * 8 */ 9 public class BS

树:BST、AVL、红黑树、B树、B+树

我们这个专题介绍的动态查找树主要有: 二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree).这四种树都具备下面几个优势: (1) 都是动态结构.在删除,插入操作的时候,都不需要彻底重建原始的索引树.最多就是执行一定量的旋转,变色操作来有限的改变树的形态.而这些操作所付出的代价都远远小于重建一棵树.这一优势在<查找结构专题(1):静态查找结构概论 >中讲到过. (2) 查找的时间复杂度大体维持在O(log(N))数量级上.可能有些结构在最差的情况下效率将

B树、B+树、红黑树、AVL树

定义及概念 B树 二叉树的深度较大,在查找时会造成I/O读写频繁,查询效率低下,所以引入了多叉树的结构,也就是B树.阶为M的B树具有以下性质: 1.根节点在不为叶子节点的情况下儿子数为 2 ~ M2.除根结点以外的非叶子结点的儿子数为 M/2(向上取整) ~ M3.拥有 K 个孩子的非叶子节点包含 k-1 个keys(关键字),且递增排列4.所有叶子结点在同一层,即深度相同 (叶节点可以看成是一种外部节点,不包含任何关键字信息) 在B-树中,每个结点中关键字从小到大排列,并且当该结点的孩子是非叶

浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的时候具有较高的灵活性,而有序数组在查找时具有较高的效率,本文介绍的二叉查找树(Binary Search Tree,BST)这一数据结构综合了以上两种数据结构的优点. 二叉查找树具有很高的灵活性,对其优化可以生成平衡二叉树,红黑树等高效的查找和插入数据结构,后文会一一介绍. 一 定义 二叉查找树(B

高级数据结构:优先队列、图、前缀树、分段树以及树状数组详解

优秀的算法往往取决于你采用哪种数据结构,除了常规数据结构,日常更多也会遇到高级的数据结构,实现要比那些常用的数据结构要复杂得多,这些高级的数据结构能够让你在处理一些复杂问题的过程中多拥有一把利器.同时,掌握好它们的性质以及所适用的场合,在分析问题的时候回归本质,很多题目都能迎刃而解了. 这篇文章将重点介绍几种高级的数据结构,它们是:优先队列.图.前缀树.分段树以及树状数组. 一.优先队列 1.优先队列的作用 优先队列最大的作用是能保证每次取出的元素都是队列中优先级别最高的,这个优先级别可以是自定

面试总结(数据库索引、B树、B+树)

1.  数据库系统维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法.这种数据结构,就是索引.索引的实现通常使用B树及其变种B+树. 创建索引可以大大提高系统的性能. 第一.通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性. 第二.可以大大加快数据的检索速度,这也是创建索引的最主要的原因. 第三.可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义. 第四.在使用分组和排序子句进行数据检索时,同样可以显著减

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

trie树(字典树)

1. trie树,又名字典树,顾名思义,它是可以用来作字符串查找的数据结构,它的查找效率比散列表还要高. trie树的建树: 比如有字符串"ab" ,"adb","adc"   可以建立字典树如图: 树的根节点head不存储信息,它有26个next指针,分别对应着字符a,b,c等.插入字符串ab时,next['a'-'a']即next[0]为空,这是申请一个结点放在next[0]的位置,插入字符串db时,next['d'-'a']即next[3]

B树、B+树、红黑树、AVL树比较

B树是为了提高磁盘或外部存储设备查找效率而产生的一种多路平衡查找树. B+树为B树的变形结构,用于大多数数据库或文件系统的存储而设计. B树相对于红黑树的区别 在大规模数据存储的时候,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下的情况.为什么会出现这样的情况,我们知道要获取磁盘上数据,必须先通过磁盘移动臂移动到数据所在的柱面,然后找到指定盘面,接着旋转盘面找到数据所在的磁道,最后对数据进行读写.磁盘IO代价主要花费在查找所需的柱面上,树的深度过大会造成磁盘IO频繁读

B树、B-树、B+树、B*树

B树.B-树.B+树.B*树 B树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中: 否则,如果查询关键字比结点关键字小,就进入左儿子:如果比结点关键字大,就进入 右儿子:如果左儿子或右儿子的指针为空,则报告找不到相应的关键字: 如果B树的所有非叶子结点的左右子树的结点数目均保持差