hdu 4871 Shortest-path tree

            自己写的第一个树分治,mark一下~~

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define lowbit(x) (x&(-x))
#define LLINF 0x3f3f3f3f3f3f3f3fll
#define CLR(a, b) memset(a, b, sizeof(a))
using namespace std;

#ifdef __int64
typedef __int64 LL;
#else
typedef long long LL;
#endif

const int maxn = 100100;

struct Edge
{
    int u, v, c;
    Edge(){}
    Edge(int u, int v, int c)
        :u(u), v(v), c(c){}
}E[maxn*2];

vector<pair<int, int> > G[maxn];

int fir[maxn], nxt[maxn*4], tot = 0;
int d[maxn];
bool vis[maxn];

void Add_Edge(int u, int v, int c)
{
    E[tot] = Edge(u, v, c);
    nxt[tot] = fir[u]; fir[u] = tot ++;
}

void init(int n)
{
    tot = 0; CLR(fir, -1);
    for(int i = 1; i <= n; i ++) G[i].clear();
}

int n, m, k;
int ans1, ans2;

void spfa(int s)
{
    CLR(d, INF);
    CLR(vis, false);
    d[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();q.pop();
        vis[u] = false;
        for(int i = 0; i < G[u].size(); i ++)
        {
            pair<int, int> p = G[u][i];
            int v = p.first, c = p.second;
            if(d[v] > d[u] + c)
            {
                d[v] = d[u] + c;
                if(!vis[v]) q.push(v), vis[v] = true;
            }
        }
    }
}

void dfs(int u)
{
    vis[u] = true;
    sort(G[u].begin(), G[u].end());
    for(int i = 0; i < G[u].size(); i ++)
    {
        pair<int, int> p = G[u][i];
        int v = p.first, c = p.second;
        if(vis[v]) continue;
        if(d[v] == d[u] + c)
        {
            Add_Edge(u, v, c);
            Add_Edge(v, u, c);
            dfs(v);
        }
    }
}

int cnt[maxn], root, minc, siz;
int md[maxn], tm[maxn], cc[maxn], tc[maxn], nw[maxn];

int tree_sz(int u, int fa)
{
    if(vis[u]) return 0;
    int ret = 1;
    for(int i = fir[u]; ~i; i = nxt[i])
    {
        Edge e = E[i];
        if(e.v == fa) continue;
        ret += tree_sz(e.v, u);
    }
    return ret;
}

void tree_root(int u, int fa)
{
    int mx = 0, mn = siz - 1; cnt[u] = 1;
    for(int i = fir[u]; ~i; i = nxt[i])
    {
        Edge e = E[i];
        if(e.v == fa) continue;
        if(vis[e.v]) continue;
        tree_root(e.v, u);
        mx = max(mx, cnt[e.v]); mn = min(mn, cnt[e.v]);
        cnt[u] += cnt[e.v];
    }
    if(mn <= mx) mx = max(mx, siz - cnt[u]), mn = min(mn, siz - cnt[u]);
    else swap(mn, mx);
    if(mx - mn < minc)  minc = mx - mn, root = u;
}

void tree_calc(int u, int fa, int lvl)
{
    for(int i = fir[u]; ~i; i = nxt[i])
    {
        Edge e = E[i];
        if(e.v == fa || vis[e.v]) continue;
        if(lvl == 0) for(int j = 1; tc[j]; j ++)
        {
            tm[j] = -1; tc[j] = 0;
        }
        nw[lvl + 1] = nw[lvl] + e.c;
        tree_calc(e.v, u, lvl + 1);
        if(lvl == 0) for(int j = 1; tc[j]; j ++)
        {
            if(cc[k - j - 1] && ans1 < tm[j] + md[k - j - 1])
            {
                ans1 = tm[j] + md[k - j - 1];
                ans2 = cc[k - j - 1] * tc[j];
            }
            else if(cc[k - j - 1] && ans1 == tm[j] + md[k - j - 1])
                ans2 += cc[k - j - 1] * tc[j];
        }
        if(lvl == 0) for(int j = 1; tc[j]; j ++)
        {
            if(tm[j] > md[j]) md[j] = tm[j], cc[j] = tc[j];
            else if(tm[j] == md[j]) cc[j] += tc[j];
        }
    }
    if(!lvl) return ;
    if(nw[lvl] > tm[lvl]) tm[lvl] = nw[lvl], tc[lvl] = 1;
    else if(nw[lvl] == tm[lvl]) tc[lvl] ++;
}

void tree_div(int u)
{
    siz = tree_sz(u, -1);
    if(siz < k) return ;
    minc = INF; tree_root(u, -1);
    CLR(md, -1); CLR(cc, 0);
    u = root; nw[0] = 0;
    md[0] = 0; cc[0] = 1;
    tree_calc(u, -1, 0);
    vis[u] = true;
    for(int i = fir[u]; ~i; i = nxt[i])
    {
        Edge e = E[i];
        if(vis[e.v]) continue;
        tree_div(e.v);
    }
}

void solve()
{
    init(n);
    for(int i = 0; i < m; i ++)
    {
        int u, v, c;
        scanf("%d%d%d", &u, &v, &c);
        G[u].push_back(make_pair(v, c));
        G[v].push_back(make_pair(u, c));
    }
    spfa(1);
    CLR(vis, false);
    dfs(1);
    ans1 = ans2 = 0;
    CLR(vis, false);
    tree_div(1);
    printf("%d %d\n", ans1, ans2);
}

int main()
{
//    freopen("1011.in", "r", stdin);
//    freopen("1011.txt", "w", stdout);
    int T, cas = 1;
    scanf("%d", &T);
    while(T --)
    {
        scanf("%d%d%d", &n, &m, &k);
        solve();
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值