首页 > 其他分享 >CF1935F Andrey's Tree (树上乱搞)

CF1935F Andrey's Tree (树上乱搞)

时间:2024-03-31 23:44:18浏览次数:27  
标签:Andrey fa int list top Tree dep CF1935F hld

题意:有一颗n个节点的数,需要解决以下问题:
先去掉节点 v 和与其相连的边;然后在剩余的图上加若干条边,在 (x,y) 之间连边的代价是 ∣x−y∣。求使得图连通的最小代价。
计算删除顶点 v后,每个顶点 1≤v≤n至少需要花费多少金币才能使图形重新成为一棵树,以及需要添加哪些边。

做法:首先可以发现,因为每次只删除一个点,所以猜测连接每个联通块的代价不会大于2.
那么可以把 1 的代价和 2 的代价分开来考虑。
对于当前要删除点 v ,1 的代价又能分成两种情况,
第一种是 v 的子树和其父亲联通块相连的点对。
第二种是 v 的子树之间相连的点对。

先考虑怎么处理第一种情况的点对。
对于节点u 可能会有很多点对满足第一种情况
这些满足条件的点对(x,y)符合 |x-y|=1 且 dep[lca(x,y)]<dep[u].
但是我们只需要维护,所有点对里面 dep[lca(x,y)] 最小的那个就可以了。
具体做法就是 把所有差值为 1 的点对(x,y)和 其LCA 保存到 x 和 y 节点上。
当在我们在 dfs 时,先在当前 x 节点 能匹配的 y节点里能遇到的dep最小值和需要连接的 (x,y)这条边保存下来。然后再与子树中的 dep 做比较不断的更新.
通过这样处理,我们能得到在当前 x 节点下,能连接到 父亲连通块代价为1 的点对。

现在考虑怎么处理第二种情况的点对。
我的做法是把所有 (x,y) 放到其 lca(x,y)处。
然后在 dfs 的过程中处理以 X 为 lca 的点对。
这样就能保证这些点对就是 v的子树之间相连的。
在处理这个情况的时候会遇到一个问题,就是 x 节点属于哪个子树,这个是把 子树的 dfs 序进行排序,然后二分得到具体 x 节点属于哪个子树。

处理 2的代价与上述类似。
上述我们处理完这 4 类点对:
1.子树和其父亲连通块代价为 1 的
2.子树和子树之间代价为 1 的
3.子树和其父亲连通块代价为 2 的
4.子树和子树之间代价为 2 的

处理完这4种边以后呢,后续做法就是用并查集来维护连通块的信息。
我们先优连第2 类节点。然后在连第1 类节点,在连 3 ,4类节点。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pir;
const int N = 2e5 + 5;
const int INF = 1e9;
using Edge = int;
struct HLD
{
    int n;
    vector<int> sz, top, dep, fa, in, out, seq;
    vector<vector<Edge> > g;
    int ts;
    HLD(const vector<vector<Edge> > &g, int root = 1) : n(int(g.size()) - 1), g(g)
    {
        ts = 0;
        sz.resize(n + 1);
        top.resize(n + 1);
        dep.resize(n + 1);
        fa.resize(n + 1);
        in.resize(n + 1);
        out.resize(n + 1);
        seq.resize(n + 1);
        dep[root] = 1;
        top[root] = root;
        dfs_sz(root);
        dfs_hld(root);
    }
    void dfs_sz(int u)
    {
        if (fa[u])
        {
            for(auto it = g[u].begin(); it != g[u].end(); it++)
            {
                if (*it == fa[u])
                {
                    g[u].erase(it);
                    break;
                }
            }
        }
        sz[u] = 1;
        for(auto &j : g[u])
        {
            fa[j] = u;
            dep[j] = dep[u] + 1;
            dfs_sz(j);
            sz[u] += sz[j];
            if (sz[j] > sz[g[u][0]])
                swap(j, g[u][0]);
        }
    }
    void dfs_hld(int u)
    {
        in[u] = ++ts;
        seq[in[u]] = u;
        for (auto j : g[u])
        {
            top[j] = (j == g[u][0] ? top[u] : j);
            dfs_hld(j);
        }
        out[u] = ts;
    }
    int lca(int u, int v)
    {
        while (top[u] != top[v])
        {
            if (dep[top[u]] > dep[top[v]])
            {
                u = fa[top[u]];
            }
            else
            {
                v = fa[top[v]];
            }
        }
        return dep[u] < dep[v] ? u : v;
    }
    int dist(int u, int v)
    {
        return dep[u] + dep[v] - 2 * dep[lca(u, v)];
    }
    bool in_subtree(int u, int v)
    {
        return in[v] <= in[u] && in[u] <= out[v];
    }
    int jump(int u, int k)
    {
        if (dep[u] < k)
        {
            return -1;
        }
        int d = dep[u] - k;
        while (dep[top[u]] > d)
        {
            u = fa[top[u]];
        }
        return seq[in[u] - dep[u] + d];
    }
    int rooted_lca(int a, int b, int c)
    {
        return lca(a, b) ^ lca(b, c) ^ lca(c, a);
    }
};
int f[N + 5], id[N + 5];
int leader(int x)
{
    while (x != f[x]) x = f[x] = f[f[x]];
    return x;
}
struct node
{
    int u, v, x;
};
void solve()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)f[i] = i;
    std::vector<vector<int>> g(n + 1);
    for(int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    HLD hld(g);
    std::vector<vector<pir>> Q(n + 1);
    std::vector<vector<node>> add(n + 1);
    for(int i = 1; i <= n; i++)
    {
        int u = i, v = i + 1;
        if(v <= n)
        {
            if(!(hld.fa[u] == v || hld.fa[v] == u))
            {
                int L = hld.lca(u, v);
                if(L == u || L == v)
                {
                }
                else
                {
                    Q[L].push_back({u, v});
                }
                add[u].push_back({u, v, L});
                add[v].push_back({u, v, L});
            }
        }
    }
    std::vector<vector<pir>> Q2(n + 1);
    std::vector<vector<node>> add2(n + 1);
    for(int i = 1; i <= n; i++)
    {
        int u = i, v = i + 2;
        if(v <= n)
        {
            if(!(hld.fa[u] == v || hld.fa[v] == u))
            {
                int L = hld.lca(u, v);
                if(L == u || L == v)
                {
                }
                else
                {
                    Q2[L].push_back({u, v});
                }
                add2[u].push_back({u, v, L});
                add2[v].push_back({u, v, L});
            }
        }
    }
    std::vector<vector<pir>> ans(n + 1);
    std::vector<int> sum(n + 1);
    std::vector<int>d1(n + 1, INF), d2(n + 1, INF);
    std::vector<pir> ed_fa1(n + 1), ed_fa2(n + 1);
    
    function<void(int, int)>dfs = [&](int u, int fa)
    {
        for(auto [x, y, z] : add[u])
        {
            if(hld.dep[z] < d1[u])
            {
                ed_fa1[u] = {x, y};
                d1[u] = hld.dep[z];
            }
        }
        for(auto [x, y, z] : add2[u])
        {
            if(hld.dep[z] < d2[u])
            {
                ed_fa2[u] = {x, y};
                d2[u] = hld.dep[z];
            }
        }
        for(int v : g[u])
        {
            if(v == fa)continue;
            dfs(v, u);
            if(d1[v] < d1[u])
            {
                ed_fa1[u] = ed_fa1[v];
                d1[u] = d1[v];
            }
            if(d2[v] < d2[u])
            {
                ed_fa2[u] = ed_fa2[v];
                d2[u] = d2[v];
            }
        }
        int k = 0;
        std::vector<pir> list;
        for(int v : g[u])
        {
            if(v == fa)continue;
            id[++k] = v;
            list.push_back({hld.in[v], v});
        }
        if(k)
        {
            id[0] = N + 1;
            sort(list.begin(), list.end());
            for(auto [x1, y1] : Q[u])
            {
                int p1 = upper_bound(list.begin(), list.end(), pir{hld.in[x1], INF}) - list.begin() - 1;
                int p2 = upper_bound(list.begin(), list.end(), pir{hld.in[y1], INF}) - list.begin() - 1;
                int x = list[p1].second, y = list[p2].second;
                if(leader(x) == leader(y))continue;
                f[leader(y)] = f[leader(x)];
                ans[u].push_back({x1, y1});
                sum[u]++;
            }
            for(int i = 1; i <= k; i++)
            {
                int v = id[i];
                if(leader(v) == leader(id[0]))continue;
                if(d1[v] < hld.dep[u])
                {
                    f[leader(v)] = f[leader(id[0])];
                    ans[u].push_back({ed_fa1[v].first, ed_fa1[v].second});
                    sum[u]++;
                }
            }
            for(int i = 1; i <= k; i++)
            {
                int v = id[i];
                if(leader(v) == leader(id[0]))continue;
                if(d2[v] < hld.dep[u])
                {
                    f[leader(v)] = f[leader(id[0])];
                    ans[u].push_back({ed_fa2[v].first, ed_fa2[v].second});
                    sum[u] += 2;
                }
            }
            for(auto [x1, y1] : Q2[u])
            {
                int p1 = upper_bound(list.begin(), list.end(), pir{hld.in[x1], INF}) - list.begin() - 1;
                int p2 = upper_bound(list.begin(), list.end(), pir{hld.in[y1], INF}) - list.begin() - 1;
                int x = list[p1].second, y = list[p2].second;
                if(leader(x) == leader(y))continue;
                f[leader(y)] = f[leader(x)];
                ans[u].push_back({x1, y1});
                sum[u] += 2;
            }
            for(int i = 1; i <= k; i++)f[id[i]] = id[i];
        }
    };
    dfs(1, 0);
    for(int i = 1; i <= n; i++)
    {
        cout << sum[i] << " " << ans[i].size() << endl;
        for(auto [x, y] : ans[i])cout << x << " " << y << endl;
        cout << '\n';
    }
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        solve();
    }
}
/*
*/

标签:Andrey,fa,int,list,top,Tree,dep,CF1935F,hld
From: https://www.cnblogs.com/pipipipipi43/p/18107462

相关文章

  • [hdu6647]Bracket Sequences on Tree 解题报告
    oj:https://gxyzoj.com/d/hzoj/p/3575因为自己的脑残原因,调了8个小时啊!!!切入正题Part1假定1为根,可以发现,如果u的两棵子树同构,则他们遍历的顺序不影响答案所以,就可以将子树按哈希值分类,这道题就变成了一个可重复组合问题,设\(f_i\)表示以1为根时i的方案数,\(a_i\)表示某一种哈......
  • 客快物流大数据项目(九十三):ClickHouse的ReplacingMergeTree深入了解 ClickHouse清除重
    ​ClickHouse的ReplacingMergeTree深入了解为了解决MergeTree相同主键无法去重的问题,ClickHouse提供了ReplacingMergeTree引擎,用来对主键重复的数据进行去重。删除重复数据可以使用optimize命令手动执行,这个合并操作是在后台运行的,且无法预测具体的执行时间。在使用optimize命......
  • 【前端】- 在使用Element UI 的el-tree组件时,从底层去研究如何去实现一键展开/关闭【t
    第一步:首先我们先去查看elementui官方文档,发现并没有提供这个方法,没办法,只能手写一个了,先给大家看看功能点击前效果:点击后效果:第二步:废话不多说直接上代码,然后我们简单解释下代码页面部分:这里是简单的数结构渲染,不多讲,$refs.Reftree获取的是el-tree的实例,具体作用请看下......
  • F. 0, 1, 2, Tree!
    原题链接题解1.模拟+贪心,我们一个一个点添加,一层一层遍历,每个节点对当前层的接口数的贡献是-1如果是节点2,对下一层接口数贡献为2,节点1贡献为1如果当前层接口数用完了就下一层,初始值层0设为1在时间复杂度合理的情况下无所不用其极code#include<bits/stdc++.h>#definelllo......
  • 【容器源码篇】Set容器(HashSet,LinkedHashSet,TreeSet的特点)
    文章目录⭐容器继承关系......
  • Ant Design Vue Tree 选中子节点同时半选中父级节点
    需要实现的效果:1、子菜单如果不是全部选中,一级菜单半选。2、子菜单全选,一级菜单选中。3、一级菜单选择,二级菜单全选。4、没有二级菜单,则只控制一级菜单。主要用到的属性是checked和halfCheckedKeys,通过手动控制那些菜单选中,那些半选中实现功能。**页面截图:**完整代码如......
  • 【Bitmap Index】B-Tree索引与Bitmap位图索引的锁代价比较研究
    通过以下实验,来验证Bitmap位图索引较之普通的B-Tree索引锁的“高昂代价”。位图索引会带来“位图段级锁”,实际使用过程一定要充分了解不同索引带来的锁代价情况。1.为比较区别,创建两种索引类型的测试表1)在表t_bitmap上创建位图索引SEC@ora11g>createtablet_bitmap(idnumber(1......
  • 58.容器_TreeMap容器的使用
    TreeMap容器的使用TreeMap和HashMap同样实现了Map接口,所以,对于API的用法来说是没有区别的。HashMap效率高于TreeMap;TreeMap是可以对键进行排序的一种容器,在需要对键排序时可选用TreeMap。TreeMap底层是基于红黑树实现的。在使用TreeMap时需要给定排序规则:元素自身实现比较规......
  • [669] Trim a Binary Search Tree
    极其少有的我决定自己来写一篇。我就是个脑残真的,我还在想要不要一个个pop,结果忘了这是一个BST……妈个鸡附上我的傻逼代码/**@lcapp=leetcode.cnid=669lang=cpp**[669]TrimaBinarySearchTree*/#include"General.h"structTreeNode{intval;Tr......
  • 树 Tree
    2024.3.21芝士wa参考视频:数据结构-树“种一棵树,最好的时间是十年前,其次是现在”树的定义树是由n(n≥0)个结点组成的有限集合。如果n=0,称为空树;如果n>0,则有一个特定的称之为根(root)的结点,它只有直接后继,但没有直接前驱;除根以外的其他结点划分为m(m≥0)个......