bzoj 4016: [FJOI2014]最短路径树问题

Description

给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。

solution

正解:点分治
构出最短路图,在最短路图上面尽量走编号小的点,直到所有的点都加入集合中,构成了最短路树
然后点分治即可

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
#include <vector>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=30005,inf=2e8;
int n,m,k,Head[N],nxt[N*10],to[N*10],dis[N*10],num=1,head[N];
inline void link1(int x,int y,int z){
    nxt[++num]=Head[x];to[num]=y;dis[num]=z;Head[x]=num;}
inline void link2(int x,int y,int z){
    nxt[++num]=head[x];to[num]=y;dis[num]=z;head[x]=num;}
bool v[N],d[N];int dd[N];
inline void spfa(int S){
    queue<int>q;while(!q.empty())q.pop();
    for(RG int i=0;i<=n;i++)dd[i]=inf,v[i]=0;
    q.push(S);dd[S]=0;v[S]=1;
    int x,u;
    while(!q.empty()){
        x=q.front();q.pop();
        for(RG int i=Head[x];i;i=nxt[i]){
            u=to[i];
            if(dd[x]+dis[i]<dd[u]){
                dd[u]=dd[x]+dis[i];
                if(!v[u])v[u]=1,q.push(u);
            }
        }
        v[x]=0;
    }
}
struct Grath{int x,dis,id;
    bool operator <(const Grath &pr)const{return x<pr.x;}};
vector<Grath>G[N];
struct edge{int x,y,z;}e[N*10];
int root=0,son[N]={N},sz[N],sum;bool vis[N];
inline void getroot(int x,int last){
    sz[x]=1;son[x]=0;
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];
        if(vis[u] || u==last)continue;
        getroot(u,x);
        sz[x]+=sz[u];
        if(sz[u]>son[x])son[x]=sz[u];
    }
    son[x]=Max(son[x],sum-sz[x]);
    if(son[x]<son[root])root=x;
}
int g[N],f[N],de[N],den=0;bool inde[N];
inline void upd(int x,int last,int dist,int l){
    if(l>k)return ;
    if(dist>f[l]){
        f[l]=dist,g[l]=1;
        if(!inde[l])inde[l]=true,de[++den]=l;
    }
    else if(dist==f[l])g[l]++;
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];
        if(vis[u] || u==last)continue;
        upd(u,x,dist+dis[i],l+1);
    }
}
int ans=0,cnt=0;
inline void getdis(int x,int last,int dist,int l){
    if(l>k)return ;
    if(dist+f[k-l]>ans)ans=dist+f[k-l],cnt=g[k-l];
    else if(dist+f[k-l]==ans)cnt+=g[k-l];
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];
        if(vis[u] || u==last)continue;
        getdis(u,x,dist+dis[i],l+1);
    }
}
inline void calc(int x){
    g[0]=1;
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];if(vis[u])continue;
        getdis(u,x,dis[i],2);upd(u,x,dis[i],1);
    }
    while(den)f[de[den]]=0,g[de[den]]=0,inde[de[den]]=0,den--;
}
inline void solve(int x){
    vis[x]=1;calc(x);
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];
        if(vis[u])continue;
        root=0;sum=sz[u];getroot(u,x);
        solve(root);
    }
}
bool b[N];
inline void dfs(int x){
    b[x]=1;
    for(RG int i=0,sz=G[x].size();i<sz;i++){
        int u=G[x][i].x,id=G[x][i].id;
        if(dd[x]+G[x][i].dis==dd[u] && !b[u]){
            link2(e[id].x,e[id].y,e[id].z);
            link2(e[id].y,e[id].x,e[id].z);
            dfs(u);
        }
    }
}
void work()
{
    int x,y,z;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        e[i].x=x;e[i].y=y;e[i].z=z;
        link1(x,y,z);link1(y,x,z);
        G[x].push_back((Grath){y,z,i});
        G[y].push_back((Grath){x,z,i});
    }
    for(int i=1;i<=n;i++)sort(G[i].begin(),G[i].end());
    spfa(1);dfs(1);
 
   sum=n;root=0;getroot(1,1);
    solve(root);
    printf("%d %d\n",ans,cnt);
}
 
int main()
{
    work();
    return 0;
}

posted @ 2017-12-16 15:42  PIPIBoss  阅读(198)  评论(0编辑  收藏  举报