BZOJ016 [FJOI2014]最短路径树问题

【题意】

在最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条

【分析】

首先通过dij跑出最短路径,然后dfs一遍求出求最短路径树,注意到了每个点后,要把儿子节点按照标号排序

之后就是普通的点分治,开一个桶表示到重心经过的边数为i的最大长度以及方案数。每次要先更新答案再加入桶避免重复计算。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e4+5;
const int inf=0x3f3f3f3f;
typedef pair<int,int> PII;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
vector <PII> G[maxn];
int n,m,k;
int dis[maxn],vis[maxn];
void dijkstra()
{
    for(int i=1;i<=n;i++) dis[i]=inf;
    dis[1]=0;
    priority_queue <PII> q;
    q.push(mp(-dis[1],1));
    while(!q.empty())
    {
        int u=q.top().se,v=q.top().fi;
        q.pop();
        if(-v!=dis[u]) continue;
        for(int k=0;k<G[u].size();k++)
        {
            int to=G[u][k].fi;
            if(dis[to]>dis[u]+G[u][k].se)
            {
                dis[to]=dis[u]+G[u][k].se;
                q.push(mp(-dis[to],to));
            }
        }
    }
}
int head[maxn],tot;
struct edge
{
    int to,nxt,v;
}e[maxn<<1];
void add(int x,int y,int z)
{
    e[++tot].to=y; e[tot].nxt=head[x]; e[tot].v=z; head[x]=tot;
}
void dfs(int u)
{
    sort(G[u].begin(),G[u].end());
    for(int k=0;k<G[u].size();k++)
    {
        int to=G[u][k].fi,v=G[u][k].se;
        if(dis[to]!=dis[u]+v || vis[to]) continue;
        vis[to]=1;
        add(to,u,v); add(u,to,v);
        dfs(to);
    }
}
int root,siz[maxn],size,gsiz,dep[maxn];
void findrt(int u,int fa)
{
    siz[u]=1;
    int maxsiz=0;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || vis[to]) continue;
        findrt(to,u);
        siz[u]+=siz[to];
        maxsiz=max(maxsiz,siz[to]);
    }
    maxsiz=max(maxsiz,size-siz[u]);
    if(maxsiz<gsiz)
    {
        gsiz=maxsiz;
        root=u;
    }
}
int ansdis,anscnt,cnt,num[maxn],len[maxn],t[maxn],t2[maxn];
void getdis(int u,int fa)
{
    if(dep[u]>k) return;
    t[++cnt]=dis[u]; t2[cnt]=dep[u];
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || vis[to]) continue;
        dis[to]=dis[u]+e[i].v;
        dep[to]=dep[u]+1;
        getdis(to,u);
    }
}
int solve(int u)
{
    int pre=cnt=0,res=0;
//    memset(num,0,sizeof(num));
//    memset(len,0,sizeof(len));
    num[0]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to]) continue;
        dis[to]=dis[u]+e[i].v;
        dep[to]=dep[u]+1;
        pre=cnt;
        getdis(to,u);
        for(int j=pre+1;j<=cnt;j++)
        {
            if(ansdis<t[j]+len[k-t2[j]-1])
            {
                ansdis=t[j]+len[k-t2[j]-1];
                res=num[k-t2[j]-1];
            }
            else if(ansdis==t[j]+len[k-t2[j]-1]) res+=num[k-t2[j]-1];
        }
        for(int j=pre+1;j<=cnt;j++)
        {
            if(len[t2[j]]==t[j]) ++num[t2[j]];
            if(len[t2[j]]<t[j]) num[t2[j]]=1,len[t2[j]]=t[j];
        }
    }
    for(int i=1;i<=cnt;i++) num[t2[i]]=0,len[t2[i]]=0;
    return res;
}
void divide(int u)
{
    vis[u]=1; dis[u]=dep[u]=0;
    int pre=ansdis,tmp=solve(u);
    if(pre!=ansdis) anscnt=tmp;
    else anscnt+=tmp;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to]) continue;
        size=siz[to]; root=0; gsiz=inf;
        findrt(to,0);
        divide(root);
    }
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    int x,y,z;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        G[x].pb(mp(y,z));
        G[y].pb(mp(x,z));
    }
    dijkstra();
    memset(vis,0,sizeof(vis));
    vis[1]=1; dfs(1);
    memset(vis,0,sizeof(vis));
    size=n; gsiz=inf;
    findrt(1,0);
    divide(root);
    printf("%d %d\n",ansdis,anscnt);
    return 0;
}

 

posted @ 2021-05-17 22:52  andyc_03  阅读(62)  评论(0)    收藏  举报