题目描述
给一个包含 nn 个点,mm 条边的无向连通图。从顶点 11出发,往其余所有点分别走一次并返回。
往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径 AA 为 1,32,111,32,11,路径 BB 为 1,3,2,111,3,2,11,路径 BB 字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含 kk 个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点 AA 到点 BB 的路径和点 BB 到点 AA 视为同一条路径。
输入格式
第一行输入三个正整数 n,m,kn,m,k,表示有 nn 个点 mm 条边,要求的路径需要经过 kk 个点。 接下来输入 mm 行,每行三个正整数 A_i,B_i,C_iA
i
?
,B
i
?
,C
i
?
(1\leq A_i,B_i\leq n,1\leq C_i\leq 100001≤A
i
?
,B
i
?
≤n,1≤C
i
?
≤10000),表示 A_iA
i
?
和 B_iB
i
?
间有一条长度为 C_iC
i
?
的边。数据保证输入的是连通的无向图。
输出格式
输出一行两个整数,以一个空格隔开,第一个整数表示包含 kk 个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。
输入数据 1
6 6 4
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1
输出数据 1
3 4
数据范围与提示
对于所有数据,n\leq 30000,m\leq 60000n≤30000,m≤60000,2\leq k\leq n2≤k≤n。
数据保证最短路径树上至少存在一条长度为 kk 的路径。
变更记录
因本题原题与P0087合并果子重复
本题更换为[FJOI2014] 最短路径树问题
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
const int N=3e4+10;
struct Edge
{
int v,w;
bool friend operator <(Edge n1,Edge n2){return n1.v<n2.v;}
}t;
std::vector <Edge> e[N],e0[N];
int n,m,k;
const int inf=0x3f3f3f3f;
#define P std::pair <int,int>
std::priority_queue <P,std::vector <P >,std::greater <P> > q;
int dis[N],used[N];
int head[N],to[N<<1],edge[N<<1],Next[N<<1],cnt;
void add(int u,int v,int w)
{
to[++cnt]=v,Next[cnt]=head[u],edge[cnt]=w,head[u]=cnt;
}
void disj()
{
memset(dis,0x3f,sizeof(dis));
q.push(std::make_pair(dis[1]=0,1));
while(!q.empty())
{
int u=q.top().second;
q.pop();
if(used[u]) continue;
used[u]=1;
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i].v,w=e[u][i].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push(std::make_pair(dis[v],v));
}
}
}
memset(used,0,sizeof(used));
}
void dfsbuild(int now)
{
used[now]=1;
for(int i=0;i<e0[now].size();i++)
{
int v=e0[now][i].v,w=e0[now][i].w;
if(!used[v])
{
add(now,v,w);
add(v,now,w);
dfsbuild(v);
}
}
}
void build()
{
for(int u=1;u<=n;u++)
{
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i].v,w=e[u][i].w;
if(dis[v]==dis[u]+w)
t={v,w},e0[u].push_back(t);
}
std::sort(e0[u].begin(),e0[u].end());
}
dfsbuild(1);
}
int ans,siz[N],rt,mi,mxlen[N],mx,del[N],td[N],tmxlen[N],scnt[N],tcnt[N];
int max(int x,int y){return x>y?x:y;}
void dfsroot(int now,int fa,int sz)
{
siz[now]=1;
int mx0=0;
for(int i=head[now];i;i=Next[i])
{
int v=to[i];
if(v==fa||del[v]) continue;
dfsroot(v,now,sz);
mx0=max(mx0,siz[v]);
siz[now]+=siz[v];
}
mx0=max(mx0,sz-siz[now]);
if(mx0<mi) mi=mx0,rt=now;
}
void dfs(int now,int fa,int dis,int dep)
{
if(dep>k) return;
if(mx<dis+mxlen[k-dep])
{
mx=dis+mxlen[k-dep];
ans=scnt[k-dep];
}
else if(mx==dis+mxlen[k-dep])
ans+=scnt[k-dep];
for(int i=head[now];i;i=Next[i])
{
int v=to[i];
if(v==fa||del[v]) continue;
dfs(v,now,dis+edge[i],dep+1);
}
if(tmxlen[dep]<dis)
{
tmxlen[dep]=dis;
tcnt[dep]=1;
}
else if(tmxlen[dep]==dis)
tcnt[dep]++;
}
void dfz(int now,int sz)
{
mi=1<<30;
dfsroot(now,0,sz);
now=rt;
del[now]=1;
for(int i=head[now];i;i=Next[i])
{
int v=to[i];
if(del[v]) continue;
dfs(v,now,edge[i],1);
for(int j=1;tmxlen[j]!=-inf;j++)
{
if(tmxlen[j]>mxlen[j])
{
scnt[j]=tcnt[j];
mxlen[j]=tmxlen[j];
}
else if(tmxlen[j]==mxlen[j])
scnt[j]+=tcnt[j];
tcnt[j]=0,tmxlen[j]=-inf;
}
}
if(mx<mxlen[k]) ans=scnt[k],mx=mxlen[k];
else if(mx==mxlen[k]) ans+=scnt[k];
for(int i=1;mxlen[i]!=-inf;i++) mxlen[i]=-inf;
for(int i=head[now];i;i=Next[i])
if(!del[to[i]])
dfz(to[i],siz[to[i]]);
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("wr.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int u,v,w,i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
t={v,w};
e[u].push_back(t);
t={u,w};
e[v].push_back(t);
}
disj();
build();
--k;
for(int i=0;i<=k+1;i++)
tmxlen[i]=mxlen[i]=-inf;
dfz(1,n);
printf("%d %d\n",mx,ans);
return 0;
}