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
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
令f[i]为当前处理过的子树中深度为i的 距重心最远的距离
dis[i]为正在处理的子树中深度为i的 距重心最远的距离
用f[k-i-1]+dis[i]来更新答案 顺便记录方案数即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int maxn=30000+10;
vector<int>A[maxn];
vector<int>C[maxn];
vector<int>B[maxn];
vector<int>D[maxn];
int dis[maxn];
int vis[maxn],p[maxn];
int f[maxn],tmp[maxn][2];
int n,m,k;
int g[maxn][2];
int sum,root;
int ans1=0,ans2=0;
int size[maxn];
int dep[maxn];
int tot=0;
struct T{
int dis,x;
bool operator <(const T &a)const{
if(dis==a.dis)
return x>a.dis;
return dis>a.dis;
}
};
priority_queue<T>q;
inline void spfa(){
for(int i=1;i<=n;i++)
dis[i]=0x7fffffff;
dis[1]=0;
T cl;
cl.dis=0,cl.x=1;
q.push(cl);
while(!q.empty()){
T dd=q.top();
q.pop();
if(vis[dd.x])
continue;
//printf("%d\n",dd.x);
vis[dd.x]=1;
for(int i=0;i<B[dd.x].size();i++)
if(dis[dd.x]+D[dd.x][i]<dis[B[dd.x][i]]){
dis[B[dd.x][i]]=dis[dd.x]+D[dd.x][i];
p[B[dd.x][i]]=dd.x;
T el;
el.x=B[dd.x][i];
el.dis=dis[B[dd.x][i]];
q.push(el);
}
}
for(int i=1;i<=n;i++){
//printf("%d ",dis[i]);
if(p[i]){
A[p[i]].push_back(i);
A[i].push_back(p[i]);
C[p[i]].push_back(dis[i]-dis[p[i]]);
C[i].push_back(dis[i]-dis[p[i]]);
}
}
}
inline void getroot(int x,int fa){
size[x]=1;
f[x]=0;
for(int i=0;i<A[x].size();i++){
int u=A[x][i];
if(vis[u]||u==fa)
continue;
getroot(u,x);
f[x]=max(f[x],size[u]);
size[x]+=size[u];
}
f[x]=max(f[x],sum-size[x]);
if(f[x]<f[root])
root=x;
}
inline void get_dep(int x,int d,int w,int fa){
dep[x]=d;
dis[x]=w;
if(dis[x]>tmp[dep[x]][0])
tmp[dep[x]][0]=dis[x],tmp[dep[x]][1]=1;
else if(dis[x]==tmp[dep[x]][0])
tmp[dep[x]][1]++;
size[x]=1;
for(int i=0;i<A[x].size();i++){
int u=A[x][i];
if(vis[u]||u==fa)
continue;
get_dep(u,d+1,w+C[x][i],x);
size[x]+=size[u];
}
}
inline void calc(int x){
for(int i=0;i<A[x].size();i++){
int u=A[x][i];
if(vis[u])
continue;
tot=0;
get_dep(u,1,C[x][i],x);
for(int j=1;j<k;j++){
//printf("%d ",g[k-dep[tmp[j]]-1][0]+dis[tmp[j]]);
if(g[k-j-1][0]+tmp[j][0]>ans1){
ans1=g[k-j-1][0]+tmp[j][0];
ans2=g[k-j-1][1]*tmp[j][1];
}
else if(g[k-j-1][0]+tmp[j][0]==ans1)
ans2+=g[k-j-1][1]*tmp[j][1];
}
for(int j=1;j<k;j++){
if(tmp[j][0]>g[j][0]){
g[j][0]=tmp[j][0];
g[j][1]=tmp[j][1];
}
else if(tmp[j][0]==g[j][0])
g[j][1]+=tmp[j][1];
tmp[j][0]=tmp[j][1]=0;
}
}
}
inline void work(int x){
//printf("%d\n",x);
for(int i=0;i<=k;i++)
g[i][1]=g[i][0]=0;
g[0][1]=1;
vis[x]=1;
calc(x);
for(int i=0;i<A[x].size();i++){
int u=A[x][i];
if(vis[u])
continue;
root=0,sum=size[u];
if(sum>=k){
getroot(u,x);
work(root);
}
}
}
int main(){
//freopen("dat.in","r",stdin);
//freopen("dat.out","w",stdout);
scanf("%d %d %d",&n,&m,&k);
int x,y,z;
while(m--){
scanf("%d %d %d",&x,&y,&z);
B[x].push_back(y);
D[x].push_back(z);
B[y].push_back(x);
D[y].push_back(z);
}
spfa();
memset(vis,0,sizeof(vis));
root=0,sum=n;
f[0]=0x7fffffff;
getroot(1,1);
work(root);
printf("%d %d\n",ans1,ans2);
return 0;
}