• NOIP 2015


    Day2

    T3 运输计划

    题意:有一颗(n)个点的树,每条边有长度,给出树上m条路径,你可以选择一条边,将其长度改为0,求这m条路径中最长的最短可以是多少

    (n,m le 3 imes10^5),边长不超过(1000)

    解法:

    (1.)二分答案,把一开始长度超限的求交集,若交集为0,无解,否则选交集中最长的边

    (2.)树上差分求交(端点$ -1, LCA + 2$)

    时间复杂度:(O(nlog^2n))

    (1.)考虑两两路径的交集,则发现它一定在这两路径端点的(4)个LCA中最深的两个LCA的路径中(本题可以这样做,其它题还要考虑(4)个LCA重合的情况等)。

    (2.)这样做发现二分的用处不大,可以把边从大到小排序,依次求交,然后枚举答案即可,如果交集为空或者最长的长度不够用了,就输出答案。可以用(RMQ)优化求(LCA)

    时间复杂度:(O(nlogn)) 听说用一些神奇的方法可以将(RMQ)的预处理优化到(O(n)),使总时间复杂度达到(O(n))

    对于第一种(O(nlog^2n))的解法,我们发现可以直接预处理出每对的LCA,这样时间复杂度将会降到(O(nlogn))

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define maxn 300100
    #define RE register 
    int n, m;
    int st[maxn], ed[maxn], len[maxn];
    int fir[maxn], nxt[maxn * 2], vv[maxn * 2], edge[maxn * 2];
    int tot = 0;
    int read()
    {
        int ret = 0;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            ret = ret * 10 + ch - '0';
            ch = getchar();
        }
        return ret;
    }
    void add(int u, int v, int w)
    {
        nxt[++tot] = fir[u];
        fir[u] = tot;
        vv[tot] = v;
        edge[tot] = w;
    }
    int dep[maxn], f[maxn][25], g[maxn][25];
    void Deal_first(int u, int fa)
    {
     //   printf("u = %d fa = %d
    ", u, fa);
        dep[u] = dep[fa] + 1;
        for(RE int i = 0; i <= 19; i++)
        {
            f[u][i + 1] = f[f[u][i]][i];
            g[u][i + 1] = g[u][i] + g[f[u][i]][i];
        }
        for(RE int i = fir[u]; i; i = nxt[i])
        {
            int v = vv[i];
            if(v == fa) continue;
            f[v][0] = u;
            g[v][0] = edge[i];
            Deal_first(v, u);
        }
    }
    int LCA(int x, int y, int &dis)
    {
        if(dep[x] < dep[y]) swap(x, y);
        for(RE int i = 20; i >= 0; i--)
        {
            if(dep[f[x][i]] >= dep[y])
            {
                dis += g[x][i];
        //        printf("g[x][i] = %d
    ", g[x][i]);
                x = f[x][i];
            }
       //     printf("x = %d y = %d
    ", x, y);
            if(x == y)
            {
                return x;
            }
        }
        for(RE int i = 20; i >= 0; i--)
        {
            if(f[x][i] != f[y][i])
            {
                dis += g[x][i];
                dis += g[y][i];
                x = f[x][i];
                y = f[y][i];
        //        printf("x = %d y = %d
    ", x, y);
            }
        }
        dis += (g[x][0] + g[y][0]);
        return f[x][0];
    }
    int val[maxn], dp[maxn * 2], from[maxn], lc[maxn];
    void dfs(int u, int fa)
    {
        dp[from[u]] += val[u];
        for(RE int i = fir[u]; i; i = nxt[i])
        {
            int v = vv[i];
            if(v == fa) continue;
            from[v] = i;
            dfs(v, u);
            dp[from[u]] += dp[from[v]];
        }
    }
    int lenth;
    int check(int x)
    {
        memset(val, 0, sizeof(val));
        memset(dp, 0, sizeof(dp));
        int cnt = 0;
        for(RE int i = 1; i <= m; i++)
        {
            if(len[i] > x)
            {
                cnt++;
                int u = st[i], v = ed[i];
                int xx = 0;
                val[u] += 1; val[v] += 1;
                val[lc[i]] -= 2;
            }
       //     else break;
        }
        dfs(1, 0);
        int maxx = -1;
        for(RE int i = 1; i <= tot; i++)
        {
        //    printf("dp[%d] = %d ", i, dp[i]);
            if(dp[i] >= cnt)
            {
                maxx = max(maxx, edge[i]);
            }
        }
       // printf("
    ");
       // printf("x = %d cnt = %d maxx = %d
    ", x, cnt, maxx);
        if(!cnt) return 1;
        if(maxx == -1 || lenth - maxx > x) return 0;
        return 1;
    }
    bool cmp(int x, int y)
    {
        return x > y;
    }
    int main()
    {
        n = read(); m = read();
        for(RE int i = 1; i < n; i++)
        {
            int u, v, w;
            u = read(); v = read(); w = read();
            add(u, v, w); add(v, u, w);
        }
        for(RE int i = 1; i <= m; i++)
        {
            st[i] = read(); ed[i] = read();
        }
        Deal_first(1, 0);
        for(RE int i = 1; i <= m; i++)
        {
            int dis = 0;
            int fa = LCA(st[i], ed[i], dis);
            len[i] = dis; lenth = max(lenth, len[i]);
            lc[i] = fa;
        //    printf("u = %d v = %d fa = %d
    ", st[i], ed[i], fa);
            //len[i] = dep[st[i]] + dep[ed[i]] - dep[LCA(st[i], ed[i])] * 2;
        }
       // sort(len + 1, len + m + 1, cmp);
       // for(int i = 1; i <= m; i++) printf("len[%d] = %d
    ", i, len[i]);
        int l = 0, r = 3e8, ans = 0;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            if(check(mid) == 1)
            {
                ans = mid;
                r = mid - 1;
            }
            else l = mid + 1;
        }
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    window 窗口对象 Javascript语言描述
    ASP.NET JScript公共类(非常有用)
    ASP.NET上传文件函数
    C#两种方式获取指定文件夹下所有子目录及文件
    模式窗口showModalDialog的用法总结
    DetailsView结合fileupload的使用
    JS连续向上滚动代码
    【原创】C# 递归获取指定目录的子目录及其所有文件
    【原创】C# 将虚拟目录下文件转换成DataTable
    【原创】ASP.NET C# 获取指定目录文件的排序和删除
  • 原文地址:https://www.cnblogs.com/Akaina/p/11649446.html
Copyright © 2020-2023  润新知