1. 程式人生 > >洛谷 [FJOI2014]最短路徑樹問題 解題報告

洛谷 [FJOI2014]最短路徑樹問題 解題報告

請問 span -s 強行 pre second 描述 data 整數

[FJOI2014]最短路徑樹問題

題目描述

給一個包含\(n\)個點,\(m\)條邊的無向連通圖。從頂點\(1\)出發,往其余所有點分別走一次並返回。

往某一個點走時,選擇總長度最短的路徑走。若有多條長度最短的路徑,則選擇經過的頂點序列字典序最小的那條路徑(如路徑\(A\)\(1,32,11\),路徑\(B\)\(1,3,2,11\),路徑\(B\)字典序較小。註意是序列的字典序的最小,而非路徑中節點編號相連的字符串字典序最小)。到達該點後按原路返回,然後往其他點走,直到所有點都走過。

可以知道,經過的邊會構成一棵最短路徑樹。請問,在這棵最短路徑樹上,最長的包含\(K\)個點的簡單路徑長度為多長?長度為該最長長度的不同路徑有多少條?

這裏的簡單路徑是指:對於一個點最多只經過一次的路徑。不同路徑是指路徑兩端端點至少有一個不同,點\(A\)到點\(B\)的路徑和點\(B\)到點\(A\)視為同一條路徑。

輸入輸出格式

輸入格式:

第一行輸入三個正整數\(n\),\(m\),\(K\),表示有\(n\)個點\(m\)條邊,要求的路徑需要經過\(K\)個點。

接下來輸入\(m\)行,每行三個正整數\(A_i,B_i,C_i(1\le A_i,B_i\le n,1 \le C_i\le10000)\),表示\(A_i\)\(B_i\)間有一條長度為\(C_i\)的邊。

數據保證輸入的是連通的無向圖。

輸出格式:

輸出一行兩個整數,以一個空格隔開,第一個整數表示包含\(K\)

個點的路徑最長為多長,第二個整數表示這樣的不同的最長路徑有多少條。

說明

對於所有數據\(n\le30000,m\le60000\)\(2\le K\le n\)

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


Solution

第二自然段的題意我現在都沒弄懂。

用我的理解就是先保證最短路,然後保證連邊的編號的字典序是最小的。

這個可以先把最短路圖跑出來,然後對每個點的出邊排序,從\(1\)開始跑\(DFS\),邊跑邊連邊

然後就是澱粉質了。

吐槽題意+強行拼題+寫起來不爽(不是為了了解一下最短路樹我才不寫呢)

復雜度\(O(nlogn)\)


Code:

#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;
}

2018.10.22

洛谷 [FJOI2014]最短路徑樹問題 解題報告