#P0044. [FJOI2014] 最短路径树问题

题目描述
给一个包含 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;
}
  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值