zoukankan      html  css  js  c++  java
  • [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) 了,所以说以后问题要分析仔细一点…

  • 相关阅读:
    JZOJ 3034. 【NOIP2012模拟10.17】独立集
    JZOJ 3035. 【NOIP2012模拟10.17】铁轨
    JZOJ 1259. 牛棚安排
    数位DP JZOJ 3316. 非回文数字
    JZOJ 3046. 游戏
    JZOJ 3013. 填充棋盘
    debian 安装oracle提供的java8
    java 汉字转拼音 PinYin4j
    debian ssh设置root权限登陆 Permission denied, please try again
    java并发下订单生成策略
  • 原文地址:https://www.cnblogs.com/alecli/p/10041021.html
Copyright © 2011-2022 走看看