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
  • 相关阅读:
    【已解决】allure空白页 报错 Uncaught TypeError: P.a.registerLanguage is not a function,Uncaught ReferenceError: allure is not defined问题解决方法
    【转】SQLServer查询死锁
    opencv-Mask(掩膜)
    opencv-cvtColor图像制式转换
    opencv-saturate_cast防溢出函数
    opencv-convertTo转换数据格式
    opencv-imwrite保存函数
    opencv-imshow显示函数
    opencv-setTo设置值
    我的蓝牙设备
  • 原文地址:https://www.cnblogs.com/DreamACMer/p/12208760.html
Copyright © 2011-2022 走看看