zoukankan      html  css  js  c++  java
  • 1月18日 LCA专项训练

    A. Lorenzo Von Matterhorn

    B.Minimum spanning tree for each edge

    C.Misha, Grisha and Underground

    D.Fools and Roads

    E.City Driving

    题意:给你一颗基环树(有n条边,n个点的连通图),有Q个询问,求u,v之间的距离最小是多少

    思路:对于一棵基环树而言,只要去掉其环上任意一条边(a , b),其边权为w ,剩下的边就可以构成一棵树

       对于 u,v 之间的最小距离 , 有可能由去掉的边(a , b)构成 ,也有可能不需要边(a , b)

       不需要L的情况

          ans = dis(u , v)

       需要L的情况

          ans = min(dis(u , a) + dis(v , b) + w , dis(v , a) + dis(u , b) ,w)

       两种情况取min即可。

    dfs序+RMQ做法(这个做法虽然有点麻烦,但是查询时O(1)的)

    #include<cstdio>
    #include<string.h>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<vector>
    #include<queue>
    #include<set>
    #include<map>
    #include<stack>
    #include<cctype>
    #define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
    #define mem(a,x) memset(a,x,sizeof(a))
    #define lson rt<<1,l,mid
    #define rson rt<<1|1,mid + 1,r
    #define P pair<int,int>
    #define ull unsigned long long
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 10;
    const ll mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    const long long INF = 0x3f3f3f3f3f3f3f3f;
    const double eps = 1e-7;
    int n;
    int f[maxn];
    int qx, qy, qv, cnt, num, si;
    struct node
    {
        int y, v;
        node(int a, int b)
        {
            y = a;
            v = b;
        }
    };
    vector<node>G[maxn];
    int dp[20][maxn * 2], dis[maxn], vis[maxn], pos[maxn], res[maxn];
    // res 存储欧拉序列 pos 存储每个节点第一次出现的位置 
    
    void init()
    {
        qx = qy = qv = 0;
        cnt = 0;
        num = 0;
        si = 0;
        mem(dp, 0);
        mem(dis, 0);
        mem(vis, 0);
        mem(res, 0);
        mem(pos, 0);
        for (int i = 0; i < maxn; ++i) G[i].clear();
    }
    
    int find(int x)
    {
        return x == f[x] ? f[x] : f[x] = find(f[x]);
    }
    void bset(int x, int y)
    {
        int fx = find(x), fy = find(y);
        f[fx] = fy;
    }
    void dfs(int u, int dist)
    {
        vis[u] = 1;
        dis[u] = dist;
        pos[u] = cnt;
        res[si] = u;
        dp[0][cnt++] = si++;
        for (int i = 0; i < G[u].size(); ++i)
        {
            int j = G[u][i].y;
            if (!vis[j])
            {
                dfs(j, dist + G[u][i].v);
                dp[0][cnt++] = dp[0][pos[u]];
            }
        }
    }
    
    void RMQ()
    {
        for (int i = 1; (1 << i) <= n; ++i)
        {
            for (int j = n - 1; j >= 0; --j)
            {
                int k = (1 << (i - 1));
                dp[i][j] = dp[i - 1][j];
                if (k + j < n)
                {
                    dp[i][j] = min(dp[i][j], dp[i - 1][j + k]);
                }
            }
        }
    }
    
    int cal(int i, int j)
    {
        if (i < j) swap(i, j);
        int k = 0;
        while ((1 << k) <= (i - j + 1))
            ++k;
        --k;
        k = min(dp[k][j], dp[k][i - (1 << k) + 1]);
        return res[k];
    }
    
    int  Dis(int u, int v)
    {
        int k = cal(pos[u], pos[v]);
        return dis[u] + dis[v] - dis[k] * 2;
    }
    
    int main()
    {
        while (scanf("%d", &n) != EOF)
        {
            if (n == 0) break;
            init();
            for (int i = 0; i <= n; ++i) f[i] = i;
            for (int i = 1; i <= n; ++i)
            {
                int x, y, v;
                scanf("%d %d %d", &x, &y, &v);
                x++, y++;
                int fx = find(x), fy = find(y);
                if (fx == fy)
                {
                    qx = x, qy = y, qv = v;
                    continue;
                }
                bset(x, y);
                G[x].push_back(node(y, v));
                G[y].push_back(node(x, v));
            }
            for (int i = 1; i <= n; ++i)
            {
                if (!vis[i])
                    dfs(i, 0);
            }
            n = n * 2 - 1;
            RMQ();
            int q;
            scanf("%d", &q);
            while (q--)
            {
                int x, y;
                scanf("%d %d", &x, &y);
                x++, y++;
                int ans = Dis(x, y);
                ans = min(ans, Dis(x, qx) + Dis(y, qy) + qv);
                ans = min(ans, Dis(x, qy) + Dis(y, qx) + qv);
                printf("%d
    ", ans);
            }
    
        }
        return 0;
    }
    
    /*
    
    7
    0 1 2
    0 2 3
    1 3 2
    2 3 8
    2 4 3
    3 5 1
    1 6 7
    3
    4 5
    0 6
    1 2
    0
    
    
    */
    View Code

    F.Tree chain problem

    题意:给你一颗由n个点构成的树,在给你m条链,让你选择一些不相交的链,使得权值最大

    思路:用dp[i]记录以i为根节点的子树的权值最大值 , sum[i] 表示点 i 的所有儿子的dp值的和

       考虑到动态规划的无后效性,因此给我们的链我们尽在lca(u , v)处考虑拿或不拿(u , v  为其中一条链的端点)

       这一点的状态dp[i] ,仅有两种状况

          不取 lca(u , v) = i 的这条链

            dp[i] = sum[i]

          取 lca(u , v) = i 的这条链

            dp[i] = ( sum[i] + ∑  (sum[vi] - dp[vi]) + w )  vi 为链上的每一个节点

          因为需要取 lca(u , v) = i 的这条链 因此 vi 点不能取其他的链, 因为题目要求链之间不相交

        为了快速计算  ∑  (sum[vi] - dp[vi]) ,用 dfs序 和  树状数组 来维护 ∑  (sum[vi] - dp[vi])  的前缀和

        当我们求 ∑  (sum[vi] - dp[vi]) 只需要在 i 节点加入 树状数组前 查询 区间[1 , u]  + 区间[1 , v] 的和 

    #pragma comment(linker,"/STACK:1024000000,1024000000")
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    using namespace std;
    
    const int maxn = 202020;
    const int maxb = 22;
    
    struct Node {
        int u, v, w;
        Node(int u, int v, int w) :u(u), v(v), w(w) {}
    };
    
    int n, m;
    vector<Node> que[maxn];
    vector<int> G[maxn];
    int lca[maxn][maxb], in[maxn], out[maxn], dep[maxn], dfs_cnt;
    int sumv[maxn];// 树状数组
    int dp[maxn], sum[maxn];
    
    //计算dfs序,in,out;预处理每个顶点的祖先lca[i][j],表示i上面第2^j个祖先,lca[i][0]表示父亲
    void dfs(int u, int fa, int d) {
        in[u] = ++dfs_cnt; // 获取每个点进入的时间
        lca[u][0] = fa; dep[u] = d;
        for (int j = 1; j < maxb; j++) {
            int f = lca[u][j - 1];
            lca[u][j] = lca[f][j - 1];
        }
        for (int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            if (v == fa) continue;
            dfs(v, u, d + 1);
        }
        out[u] = ++dfs_cnt; // 获取每个点出去的时间
    }
    
    // 倍增法在线求lca ,o(n*logn)预处理+o(logn)询问
    int Lca(int u, int v) {
        if (dep[u] < dep[v]) swap(u, v);
        //二进制倍增法,u,v提到相同高度
        for (int i = maxb - 1; i >= 0; i--) {
            if (dep[lca[u][i]] >= dep[v]) u = lca[u][i];
        }
        //当lca为u或者为v的时候
        if (u == v) return u;
        //lca不是u也不是v的情况
        //一起往上提
        for (int i = maxb - 1; i >= 0; i--) {
            if (lca[u][i] != lca[v][i]) {
                u = lca[u][i];
                v = lca[v][i];
            }
        }
        return lca[u][0];
    }
    
    //因为需要求出区间[in[u] , out[u]]上的(sum[i] - dp[i])的和 , 用树状数组维护
    
    int get_sum(int x) {
        int ret = 0;
        while (x > 0) {
            ret += sumv[x];
            x -= x & (-x);
        }
        return ret;
    }
    
    void add(int x, int v) {
        while (x < maxn) {
            sumv[x] += v;
            x += x & (-x);
        }
    }
    
    //树形dp(用到dfs序和树状数组来快速计算链)
    
    void solve(int u, int fa) {
        for (int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            if (v == fa) continue;
            solve(v, u);
            sum[u] += dp[v];
        }
        dp[u] = sum[u]; // 先将dp[u]处理为不选择以u为lca的链
        for (int i = 0; i < que[u].size(); i++) {
            Node nd = que[u][i];
            //get_sum(in[nd.u])处理的是lca(u,v)到u点这条路径的所有顶点
            //get_sum(in[nd.v])处理的是lca(u,v)到v点这条路径的所有顶点
            dp[u] = max(dp[u], sum[u] + get_sum(in[nd.u]) + get_sum(in[nd.v]) + nd.w);
        }
        add(in[u], sum[u] - dp[u]);
        add(out[u], dp[u] - sum[u]);
    }
    
    void init() {
        dfs_cnt = 0;
        for (int i = 1; i <= n; i++) G[i].clear();
        for (int i = 1; i <= n; i++) que[i].clear();
        memset(lca, 0, sizeof(lca));
        memset(sumv, 0, sizeof(sumv));
        memset(sum, 0, sizeof(sum));
        memset(dp, 0, sizeof(dp));
    }
    
    int main() {
        int tc;
        scanf("%d", &tc);
        while (tc--) {
            scanf("%d%d", &n, &m);
            init();
            for (int i = 1; i < n; i++) {
                int u, v;
                scanf("%d%d", &u, &v);
                G[u].push_back(v);
                G[v].push_back(u);
            }
            dfs(1, 0, 1);
            while (m--) {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                //每条链在Lca的位置上处理,这样符合dp的无后效性
                que[Lca(u, v)].push_back(Node(u, v, w));
            }
            solve(1, 0);
            printf("%d
    ", dp[1]);
        }
        for (int i = 1; i <= n; ++i)
        {
            printf("%d ", dp[i]);
        }
        printf("
    ");
        for (int i = 1; i <= n; ++i)
        {
            printf("%d ", sum[i]);
        }
        printf("
    ");
        return 0;
    }
    View Code
  • 相关阅读:
    【题解】【BT】【Leetcode】Populating Next Right Pointers in Each Node
    【题解】【BT】【Leetcode】Binary Tree Level Order Traversal
    【题解】【BST】【Leetcode】Unique Binary Search Trees
    【题解】【矩阵】【回溯】【Leetcode】Rotate Image
    【题解】【排列组合】【素数】【Leetcode】Unique Paths
    【题解】【矩阵】【回溯】【Leetcode】Unique Paths II
    【题解】【BST】【Leetcode】Validate Binary Search Tree
    【题解】【BST】【Leetcode】Convert Sorted Array to Binary Search Tree
    第 10 章 判断用户是否登录
    第 8 章 动态管理资源结合自定义登录页面
  • 原文地址:https://www.cnblogs.com/DreamACMer/p/12208760.html
Copyright © 2011-2022 走看看