• [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) 了,所以说以后问题要分析仔细一点…

  • 相关阅读:
    置换群
    背包问题
    并查集
    链式前向星
    一个简单的金额平均分配函数(C#版)
    EasyUI ComboGrid的绑定,上下键和回车事件,输入条件查询
    Oracle表解锁语句
    如何将两个json合并成一个
    textbox只能输入数字或中文的常用正则表达式和验证方法
    C#注册表的读,写,删除,查找
  • 原文地址:https://www.cnblogs.com/alecli/p/10041021.html
Copyright © 2020-2023  润新知