【BZOJ-4016】最短路徑樹問題 Dijkstra + 點分治

4016: [FJOI2014]最短路徑樹問題

Time Limit: 5 Sec  Memory Limit: 512 MB
Submit: 1092  Solved: 383
[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

HINT

對於全部數據n<=30000,m<=60000,2<=K<=n。

數據保證最短路徑樹上至少存在一條長度爲K的路徑。

2016.12.7新加數據一組by - wyx-150137

Source

Solution

這道題仍是比較好搞的。php

首先按照題目的意思搞出 最短路徑樹 來,這裏利用vector排序搞了一下字典序的問題。ios

而後就能夠用 點分治 處理答案了。ide

令$dp[dep][0/1]$表示通過點數爲$dep$的最長路徑的長度和方案數,而後DFS一遍就能夠了。ui

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
	while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}
#define MAXN 100010
#define LL long long

int N,M,K;
LL Dist,Num;

vector<int> G[MAXN];

namespace Graph{
	
	struct EdgeNode{
		int next,to,dis,from;
	}edge[MAXN<<1];
	int head[MAXN],cnt;

	inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].from=u; edge[cnt].dis=w;}
	inline void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,w);}

	#define Pa pair<int,int>
	#define MP make_pair
	#define INF 0x7fffffff
	priority_queue<Pa,vector<Pa>,greater<Pa> >q;
	int dist[MAXN];
	inline void Dijkstra(int S=1)
	{
		for (int i=1; i<=N; i++) dist[i]=INF;
		q.push(MP(0,S)); dist[S]=0;
		while (!q.empty()) {
			int dis=q.top().first;
			int now=q.top().second;
			q.pop();
			if (dis>dist[now]) continue;
			for (int i=head[now]; i;i=edge[i].next) {
				if (dist[edge[i].to]>dis+edge[i].dis) {
					dist[edge[i].to]=dis+edge[i].dis;
					q.push(MP(dist[edge[i].to],edge[i].to));
				}
			}
		}
		
		for (int i=1; i<=cnt; i++) {
			int u=edge[i].from,v=edge[i].to,d=edge[i].dis;
			if (dist[u]+d==dist[v]) G[u].push_back(v);
		}
		
//		for (int i=1; i<=N; printf("Now=%d\n",i),i++)
//			for (int j=0; j<G[i].size(); j++)
//				printf("%d  ",G[i][j]);
	}
}

namespace TreeDivide{

	struct EdgeNode{
		int next,to,dis;
	}edge[MAXN<<1];
	int head[MAXN],cnt;
	
	inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].dis=w;}
	inline void InsertEdge(int u,int v,int w) {/*printf("<%d  %d>\n",u,v,w);*/ AddEdge(u,v,w); AddEdge(v,u,w);}
	
	int size[MAXN],mx[MAXN],root,Sz;
	bool visit[MAXN];
	inline void Getroot(int now,int last)
	{
		size[now]=1; mx[now]=0;
		for (int i=head[now]; i; i=edge[i].next)
			if (edge[i].to!=last && !visit[edge[i].to]) {
				Getroot(edge[i].to,now);
				size[now]+=size[edge[i].to];
				mx[now]=max(mx[now],size[edge[i].to]);
			}
		mx[now]=max(mx[now],Sz-size[now]);
		if (mx[now]<mx[root]) root=now;
	}
	
	int f[MAXN][2],g[MAXN][2];
	inline void DFS(int now,int last,int dep,int dis)
	{
		if (dep>K) return;
		if (dis>f[dep][0]) 
			f[dep][0]=dis,f[dep][1]=1;
		else 
			if (dis==f[dep][0]) 
				f[dep][1]++;

		for (int i=head[now]; i; i=edge[i].next)
			if (edge[i].to!=last && !visit[edge[i].to]) {
				DFS(edge[i].to,now,dep+1,dis+edge[i].dis);
			}
	}
	
	inline void Divide(int now)
	{
		visit[now]=1;
		for (int i=1; i<=K; i++) g[i][0]=g[i][1]=0;
		g[0][0]=0; g[0][1]=1;
		
		K--;
		for (int i=head[now]; i; i=edge[i].next)
			if (!visit[edge[i].to]) {
				
				for (int j=1; j<=K; j++) f[j][0]=f[j][1]=0;
				
				DFS(edge[i].to,now,1,edge[i].dis);
				
				for (int j=1; j<=K; j++) {
					if (Dist<g[K-j][0]+f[j][0])
						Dist=g[K-j][0]+f[j][0],Num=(LL)g[K-j][1]*f[j][1];
					else
						if (Dist==g[K-j][0]+f[j][0])
							Num+=(LL)g[K-j][1]*f[j][1];
				}
				
				for (int j=1; j<=K; j++) {
					if (g[j][0]<f[j][0])
						g[j][0]=f[j][0],g[j][1]=f[j][1];
					else
						if (g[j][0]==f[j][0])
							g[j][1]+=f[j][1];
				}
			}
		K++;
		
		
		for (int i=head[now]; i; i=edge[i].next) 
			if (!visit[edge[i].to]) {
				root=0;
				Sz=size[edge[i].to];
				Getroot(edge[i].to,now);
				Divide(root);
			}
	}
	
	bool mark[MAXN];
	inline void BuildTree(int now)
	{
		mark[now]=1;
		sort(G[now].begin(),G[now].end());
		for (int i=0; i<G[now].size(); i++) 
			if (!mark[G[now][i]]) {
			BuildTree(G[now][i]);
			InsertEdge(now,G[now][i],Graph::dist[G[now][i]]-Graph::dist[now]);
		}
	}
}using namespace TreeDivide;

int main()
{
	N=read(),M=read(),K=read();
	for (int i=1,x,y,z; i<=M; i++) {
		x=read(),y=read(),z=read();
		Graph::InsertEdge(x,y,z);
	}
	Graph::Dijkstra();
	TreeDivide::BuildTree(1);
	
	Sz=mx[root=0]=N;
	TreeDivide::Getroot(1,0);
	TreeDivide::Divide(root);

	printf("%lld %lld\n",Dist,Num);
	return 0;
}
相關文章
相關標籤/搜索