[FJOI2014]最短路径树问题

题意

Here

思考

吐槽一下这个题,完全就是两个裸题拼一起了,而且两个板子之间毫无联系…

首先我们造一个保证字典序最小的最短路径树,怎么保证字典序呢,先将你存的图按字典序从小到大重新排个序再跑最短路就行了。之后就是:跑 \(dijkstra\)\(dfs\) 一遍重新建图,如果 \(u\) 已经被访问过,那么边 \(e\{u,v,d\}\) 在最短路上的当且仅当:\(dist[u] + d == dist[v]\)

建完了最短路树之后,问题就是 询问一颗树上的经过点数等于 \(k\) 的路径长度的最大值,以及最长路径的条数(路径必须满足经过的点数等于 \(k\))这个就是点分治裸题了。

代码

#include<bits/stdc++.h>
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x * f;
}
const int N = 30030;
const int M = 60060;
vector<pair<int, int> > G[N];
struct node{
    int nxt, to, dis;
}E[M << 1];
int H[N], num2;
void RB(int from, int to, int dis){
    E[++num2].nxt = H[from];
    E[num2].to = to;
    E[num2].dis = dis;
    H[from] = num2;
}
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > >q;
int dist[N], vis[N];
void dijkstra(){
    memset(vis, 0, sizeof(vis));
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    q.push( make_pair(0, 1) );
    while(!q.empty()){
        int u = q.top().second; q.pop();
        if(vis[u]) continue; vis[u] = 1;
        for(pair<int, int> V : G[u]){
            int v = V.first, d = V.second;
            if(dist[v] > dist[u] + d){
                dist[v] = dist[u] + d;
                q.push( make_pair(dist[v], v) );
            }
        }
    }
}
void rebuild(int u){
    vis[u]=1;
    for(pair<int, int> V : G[u])
    {
        int v = V.first, d = V.second;
        if(vis[v] || dist[u] + d != dist[v]) continue;
        RB(u, v, d); RB(v, u, d); rebuild(v);
    }
}
int root, sum, sz[N], f[N], S[N], num[N], ans, ans2, MAXD, n, m, k;
void getroot(int u, int fa){
    sz[u] = 1; f[u] = 0;
    for(int i=H[u]; i; i=E[i].nxt){
        int v = E[i].to;
        if(vis[v] || v == fa) continue;
        getroot(v, u);
        sz[u] += sz[v];
        f[u] = max(f[u], sz[v]);
    }
    f[u] = max(f[u], sum - sz[u]);
    if(f[root] > f[u]) root = u;
}
void dfs(int u, int fa, int now){
    MAXD = max(MAXD, now);
    if(now == k - 1){
        if(ans == dist[u]) ans2 ++;
        if(dist[u] > ans){
            ans2 = 1;
            ans = dist[u];
        }
        return;
    }
    int nowans = -1;
    if(S[k - 1 - now] != -1) nowans = dist[u] + S[k - 1 - now];
    if(ans == nowans) ans2 += num[k - 1 - now];
    if(nowans > ans){
        ans2 = num[k - 1 - now];
        ans = nowans;
    }
    for(int i=H[u]; i; i=E[i].nxt){
        int v = E[i].to;
        if(vis[v] || v == fa) continue;
        dist[v] = dist[u] + E[i].dis;
        dfs(v, u, now + 1);
    }
}
void update(int u, int fa, int now){
    if(now == k - 1) return;
    if(S[now] == dist[u]) num[now] ++;
    else S[now] = max(S[now], dist[u]), num[now] = 1;
    for(int i=H[u]; i; i=E[i].nxt){
        int v = E[i].to;
        if(vis[v] || v == fa) continue;
        update(v, u, now + 1);
    }
}
void solve(int u){
    MAXD = 0;
    vis[u] = 1;
    for(int i=H[u]; i; i=E[i].nxt){
        int v = E[i].to;
        if(vis[v]) continue;
        dist[v] = E[i].dis;
        dfs(v, u, 1);
        update(v, u, 1);
    }
    for(int i=1; i<=MAXD; i++) S[i] = -1, num[i] = 0;
    for(int i=H[u]; i; i=E[i].nxt){
        int v = E[i].to;
        if(vis[v]) continue;
        sum = sz[v]; root = 0;
        getroot(v, u);
        solve(root);
    }
}
int main(){
    f[0] = 0x3f3f3f3f;
    n = read(), m = read(), k = read();
    for(int i=1; i<=m; i++){
        int u = read(), v = read(), d = read();
        G[u].push_back( make_pair(v, d) );
        G[v].push_back( make_pair(u, d) );
    }
    for(int i=1; i<=n; i++){
        sort( G[i].begin(), G[i].end() );
    }
    dijkstra();
    memset(vis, 0, sizeof(vis));
    rebuild(1);
    sum = n; root = 0;
    memset(vis, 0, sizeof(vis));
    memset(dist, 0, sizeof(dist));
    memset(S, -1, sizeof(S));
    getroot(1, 0);
    solve(root);
    cout << ans << " " << ans2;
    return 0;
}

总结

虽说是点分治裸题不过我还是在写的时候犯了一个小错误,就是我之前没有记录 \(num[]\)数组,然后样例一直输出 \(3\ 2\),玩了样例之后才发现自己 \(sb\) 了,所以说以后问题要分析仔细一点…

posted @   alecli  阅读(222)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示