首页 > 其他分享 >洛谷P2680 运输计划(LCA + 二分 + 树上边差分)

洛谷P2680 运输计划(LCA + 二分 + 树上边差分)

时间:2022-12-22 21:46:01浏览次数:59  
标签:二分 洛谷 anc int lca P2680 read LCA dis

洛谷P2680 运输计划

​ 现在有一棵树,每条树边上都有正权值。接下来,有 m 个询问,每次询问给出两个结点,这两个结点之间有一条路径。现在你可以任选一条树边,将其边权置为0,请输出询问中的路径的最大值最小值是多少。

思路:

​ 看到使最大值最小,就在向二分的想法贴。我们可以预处理出 m 个询问的每个询问的路径长度。想了想二分答案的话,那check函数就只能是\(O(m)\)吧。二分一下这个最大路径的最小值,遍历一遍这些预处理好的边长度,若长度大于二分的答案,那就是需要被删边的路径,我们算一下 m 里有 cnt 个路径是需要被删边的。对边进行差分(利用树的每个结点至多有一个父亲的性质,把父亲的边权归到自己作为点权),差分后求一个前缀和,枚举所有差分数组值等于 cnt 的边,取这些边的最大权值,若最大的查询路径长度减去这个最大边权后小于等于二分答案,那就是合法的。

代码

​ 本题的二分做法会卡常,请使用链式前向星。

#include <bits/stdc++.h>
    
using namespace std;
const int N = 300010;

int n, m;
vector<array<int, 4> > vc; //x, y, lca, dis(x, y)
int dis[N], dep[N], val[N];
int anc[N][18];
int d[N];
int max_dis;

int nex[N << 1], e[N << 1], wei[N << 1], h[N], tot = 2;
void add(int a, int b, int c)
{
    e[tot] = b;
    wei[tot] = c;
    nex[tot] = h[a];
    h[a] = tot ++;1
}

void dfs2(int u, int fa)
{
    for(int i = h[u]; i; i = nex[i])
    {
        int v = e[i];
        if(v == fa) continue;
        dfs2(v, u);
        d[u] += d[v];
    }
}

bool check(int mid)
{
    memset(d, 0, sizeof d);
    int res = 0;
    int cnt = 0;
    for(auto [x, y, lca, dist] : vc)
    {
        if(dist > mid)
        {
            d[x] ++, d[y] ++, d[lca] -= 2;
            cnt ++;
        }
    }

    dfs2(1, 0);
    for(int i = 2; i <= n; i ++)
    {
        if(d[i] == cnt)
            res = max(res, val[i]);
    }
    return max_dis - res <= mid;
}

void dfs1(int u, int fa)
{
    dep[u] = dep[fa] + 1;
    anc[u][0] = fa;
    for(int i = 1; (1 << i) <= dep[u]; i ++)
        anc[u][i] = anc[anc[u][i - 1]][i - 1];

    for(int i = h[u]; i; i = nex[i])
    {
        int v = e[i], w = wei[i];
        if(v == fa) continue;
        val[v] = w;
        dis[v] = dis[u] + w;
        dfs1(v, u);
    }
}

int LCA(int x, int y)
{
    if(dep[x] < dep[y])
        swap(x, y);
    for(int i = 17; i >= 0; i --)
        if(dep[x] - (1 << i) >= dep[y])
            x = anc[x][i];
    if(x == y)
        return x;
    for(int i = 17; i >= 0; i --)
        if(anc[x][i] != anc[y][i])
            x = anc[x][i], y = anc[y][i];
    return anc[x][0];
}

void init()
{
    for(auto &[x, y, lca, dist] : vc)
    {
        lca = LCA(x, y);
        dist = dis[x] - 2 * dis[lca] + dis[y];
        max_dis = max(max_dis, dist);
    }
}

signed main()
{
    read(n); read(m);
    vc.resize(m);
    for(int i = 1; i < n; i ++)
    {
        int x, y, c;
        read(x); read(y); read(c);
        add(x, y, c);
        add(y, x, c);
    }
    dfs1(1, 0);

    for(int i = 0; i < m; i ++)
    {
        int x, y;
        read(x); read(y);
        vc[i] = {x, y, 0, 0};
    }
    init();

    int l = 0, r = max_dis;
    while(l < r)
    {
        int mid = (l + r) >> 1;
        if(check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    printf("%d\n", r);
}

标签:二分,洛谷,anc,int,lca,P2680,read,LCA,dis
From: https://www.cnblogs.com/DM11/p/16999631.html

相关文章

  • 洛谷 P5401 [CTS 2019] 珍珠 题解
    题目链接令\(c_i\)表示第i种颜色的珍珠的数量,显然我们最多能装的瓶数是\(\sum\lfloor\frac{c_i}2\rfloor\)。也就是说,\(c_i\)为奇数的\(i\)的数量不能太多,这个数量要......
  • 洛谷 P6580 [Ynoi 2019] 美好的每一天~ 不连续的存在 题解
    既然是YNOI那肯定是要分块的。先考虑树是一条链的情况,可以直接回滚莫队,对n个节点组成的序列分块。在回滚莫队的过程中,当前维护的区间一共会扩展\(O(n\sqrtn)\)次,每次都是......
  • 洛谷 P1712 [NOI2016] 区间
    很多套路糅合在一起的一道题。记\(len_i=r_i-l_i\)。则所求转化为一个有\(m\)个区间的集合\(S\),满足:存在一个\(x\),使得对于所有\(S\)中的区间\(i\),有\(l_......
  • 洛谷P3224 [HNOI2012]永无乡 题解 splay tree 启发式合并
    题目链接:https://www.luogu.com.cn/problem/P3224主要知识点是:树上启发式合并,即每次合并将小的树里面的每个点合并大大的树里面,时间复杂度\(O(n\log^2n)\)。同时需要......
  • 洛谷-P1450 硬币购物
    P1450硬币购物容斥||\(dp\)+单调队列优化容易看出是个多重背包,然后拿单调队列优化一下后,计算量为\(O(4ns)\)这种做法的话就是单调队列优化板子题#include<bits/......
  • 洛谷 P1020 导弹拦截(递增子串 DP )
    题目大意:有一个数列,我们要获取一组子串,这个子串必须单调不增。问:(1)最长我们可以获取多长的这种子串(2)这个数列中最多有多少种这种子串解题思路:其中问题一是很典型的DP问题,最......
  • 洛谷 P1087 FBI树
    题目大意:已知一棵树由字符串组成,规定:若节点全1把这节点称为I节点。若节点全0把节点称为B节点。若节点含0同时含1把节点称为F节点。现在有一个字符串N长,左子树是前一半字......
  • 洛谷 P1088 火星人(乱搞)
    题目大意:已知有一串数字,问在原来的数字串的字典序加m后,应该输出多少解题思路:最简便的做法是用next_permutation,这个生成的全排列可以按照字典序递增,这里我用的是搜索的方法......
  • 洛谷 P1113 杂务(拓扑排序,递归)
    题目大意:有一个有向无圈图,每个节点看作一个任务,一个任务需要完成必须先完成父亲节点的任务,每个任务都有耗时。假设现在所有不相关任务都可以并行执行,问最短多少时间可以把所......
  • 洛谷 P1996 约瑟夫问题
    问题简介:小朋友围城一圈,从1号开始报数,报到M的那位同学出局,然后下一位同学报1,循环上述过程直到所有同学出局。输出依次出局的小朋友的号码解题思路:没什么好说的,就是模拟,出局......