HDU 4871 Shortest-path tree (树分治之点分治 最短路径树问题)

题目大意:

给出一个连通的无向图, 然后找一颗生成树, 生成树满足点1到每个点的书上距离都是原图中的最短路, 然后在满足最短路的条件下, 点1到每个点的树上路径的序列字典序最小

找出这个树之后, 给出K询问树上有K个结点的路径的最大边权值, 以及这样的最大边权值的路径的方案数


大致思路:

首先spfa取得所有的1到其他点的最短距离

然后按照字典序进行dfs找出最短路径树

之后就是裸的树分治的问题了

用dis[i]表示i个结点的路径的最大长度, 用一个pre相加表示是否是当前层的子树的结果


代码如下:

Result  :  Accepted     Memory  :  7208 KB     Time  :  908 ms

/*
 * Author: Gatevin
 * Created Time:  2015/10/14 19:53:35
 * File Name: Sakura_Chiyo.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

#define maxn 30010

int Test;
int n, m, K;
vector<pair<int, int> > T[maxn];//找出来的树

vector<pair<int, int> > G[maxn];//原图

int del[maxn];
int root;
int num;
int mx[maxn];
int size[maxn];
int mi;

void dfs_size(int now, int father)
{
    size[now] = 1;
    mx[now] = 0;
    for(int i = 0, sz = T[now].size(); i < sz; i++)
    {
        int v = T[now][i].first;
        if(v != father && !del[v])
        {
            dfs_size(v, now);
            size[now] += size[v];
            if(size[v] > mx[now]) mx[now] = size[v];
        }
    }
}

void dfs_root(int r, int now, int father)
{
    if(size[r] - size[now] > mx[now]) mx[now] = size[r] - size[now];
    if(mx[now] < mi) mi = mx[now], root = now;
    for(int i = 0, sz = T[now].size(); i < sz; i++)
    {
        int v = T[now][i].first;
        if(v != father && !del[v]) dfs_root(r, v, now);
    }
}

bool vis[maxn];
int d[maxn];
const int inf = 1e9;

void spfa()//跑点1到其他所有点的最短距离为d[]
{
    queue<int> Q;
    Q.push(1);
    memset(vis, 0, sizeof(vis));
    fill(d, d + n + 1, inf);
    vis[1] = 1, d[1] = 0;
    while(!Q.empty())
    {
        int now = Q.front();
        Q.pop();
        vis[now] = 0;
        for(int i = 0, sz = G[now].size(); i < sz; i++)
        {
            int v = G[now][i].first;
            if(d[v] > d[now] + G[now][i].second)
            {
                d[v] = d[now] + G[now][i].second;
                if(!vis[v]) vis[v] = 1, Q.push(v);
            }
        }
    }
}

void getTree(int now)
{
    for(int i = 0, sz = G[now].size(); i < sz; i++)
        if(d[now] + G[now][i].second == d[G[now][i].first] && !vis[G[now][i].first])
        {
            vis[G[now][i].first] = 1;
            T[now].push_back(G[now][i]);
            T[G[now][i].first].push_back(make_pair(now, G[now][i].second));
            getTree(G[now][i].first);
        }
}

#define count motherfuck

lint dis[maxn];//dis[i]表示经过i个结点的路径最长的长度
int count[maxn];//count[i]表示经过i个结点的路径最长的方案数
int ans;//点数为K个点路径最大长度
int cnt;//cnt表示dis[K]的答案方案数
lint pre = 0;
lint bit = 4e8;

void get(int now, int father, int num, lint dep, int flag)//num表示经过点数,dep表示到根节点距离
{
    num++;
    if(num >= K) return;
    if(flag == 0)
    {
        if(dis[K - num] >= pre)
        {
            if(ans < (dis[K - num] - pre) + dep)
                ans = dis[K - num] - pre + dep, cnt = count[K - num];
            else if(ans == (dis[K - num] - pre) + dep)
                cnt += count[K - num];
        }
    }
    else
    {
        if(dis[num] >= pre)
        {
            if(dis[num] < pre + dep)
            {
                dis[num] = pre + dep;
                count[num] = 1;
            }
            else if(dis[num] == pre + dep)
                count[num]++;
        }
        else dis[num] = pre + dep, count[num] = 1;
    }
    for(int i = 0, sz = T[now].size(); i < sz; i++)
    {
        int v = T[now][i].first;
        if(!del[v] && v != father)
            get(v, now, num, dep + T[now][i].second, flag);
    }
}

void dfs(int now)
{
    mi = n;
    dfs_size(now, 0);
    dfs_root(now, now, 0);
    del[root] = 1;
    pre += bit;
    dis[1] = pre;
    count[1] = 1;
    for(int i = 0, sz = T[root].size(); i < sz; i++)
    {
        int v = T[root][i].first;
        if(!del[v])
        {
            get(v, root, 0, (lint)T[root][i].second, 0);
            get(v, root, 1, (lint)T[root][i].second, 1);
        }
    }
    int tmpRoot = root;
    for(int i = 0, sz = T[tmpRoot].size(); i < sz; i++)
    {
        int v = T[tmpRoot][i].first;
        if(!del[v])
            dfs(v);
    }
}

void solve()
{
    //pre = 0;
    //memset(dis, 0, sizeof(dis));
    spfa();
    memset(vis, 0, sizeof(vis));
    vis[1] = 1;
    getTree(1);//把树找出来
    ans = 0;
    cnt = 0;
    memset(del, 0, sizeof(del));
    dfs(1);
    printf("%d %d\n", ans, cnt);
}

int main()
{
    scanf("%d", &Test);
    while(Test--)
    {
        scanf("%d %d %d", &n, &m, &K);
        for(int i = 0; i <= n; i++) G[i].clear(), T[i].clear();
        int u, v, w;
        for(int i = 0; i < m; i++)
        {
            scanf("%d %d %d", &u, &v, &w);
            G[u].push_back(make_pair(v, w));
            G[v].push_back(make_pair(u, w));
        }
        for(int i = 1; i <= n; i++)
            sort(G[i].begin(), G[i].end());
        solve();
    }
    return 0;
}
/*
2
2 1 2
1 2 3
*/


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值