Codeforces 1339D - Edge Weight Assignment (数据结构 - 树)

Description

思路

这题总觉得有些想法,但是就是写不出来。看了题解好久才想明白。(以下均图片来自cf题解)

每个度数大于2的结点都是某些叶子结点的LCA,设这个结点为图中的C。虚线代表从叶子结点到C的路径,把路径上面的边全部合并,看成一条边。合并的边的权值就是路径上的边的权值的异或。

对树进行这样的处理之后,一个点要么是叶子结点,要么是LCA点(设为C点)。如果是C点,那它要么连接叶子结点,要么连接另一个C点。

根据题意,xor(path(leaf1, C)) == xor(path(leaf2, C)) == xor(path(root, C))

由于C点可能连接多个叶子结点,显然,所有path(leaf, C)权重都要相等,因此把这些叶子结点都合并为一个(即可以把leaf1,leaf2, 合成一个结点)。

这样,任何的树都可以转化为下面这些图这种形式。

分情况讨论

  1. 对于最小的f,总是可以用不多于3个数字。如果每个叶子点对之间的距离都是偶数(即所有叶子点奇偶性都是偶数),那么最小f为1,否则为3。

  2. 对于最大的f,见图和cf题解很容易知道成立。最大的f为所有边的个数减去(每个C点连接的叶子点个数(未合并前) - 1)

#include <bits/stdc++.h>
using namespace std;
#define endl ‘\n‘
typedef long long ll;
const int N = 1e5 + 10;

vector<int> np[N];
int cnt[N];
int p[N];

void dfs(int u, int fa, int par) {
    p[u] = par;
    for(auto v : np[u]) {
        if(v == fa) continue;
        dfs(v, u, par ^ 1);
    }
}

int main() {
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    for(int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        np[u].push_back(v);
        np[v].push_back(u);
        cnt[u]++;
        cnt[v]++;
    }

    int mn = 1, mx = n - 1;
    for(int i = 1; i <= n; i++) {
        if(cnt[i] == 1) {
            dfs(i, -1, 0);
            break;
        }
    }

    for(int i = 1; i <= n; i++) {
        if(cnt[i] == 1 && p[i] == 1) {
            mn = 3;
            break;
        }
    }

    for(int i = 1; i <= n; i++) {
        int ch = 0;
        for(auto v : np[i]) {
            if(cnt[v] == 1) {
                ch++;
            }
        }
        if(ch != 0)
            mx -= (ch - 1);
    }

    cout << mn << " " << mx << endl;
}

原文地址:https://www.cnblogs.com/limil/p/12690898.html

时间: 2024-10-12 04:15:24

Codeforces 1339D - Edge Weight Assignment (数据结构 - 树)的相关文章

Codeforces 827D Best Edge Weight 倍增 + 并查集 || 倍增 + 压倍增标记 (看题解)

Best Edge Weight 我们先找出一棵最小生成树, 对于非树边来说, 答案就是两点路径上的最大值 - 1, 这个直接倍增就能处理. 对于树边来说, 就是非树边的路径经过这条边的最小值 - 1, 这个可以用并查集压缩路径 或者 更压st表一样的方式更新. 感觉就是没想到先扣出来一个最小生成树, 而是往克鲁斯卡尔的过程中想了. #include<bits/stdc++.h> #define LL long long #define LD long double #define ull u

Codeforces 219D. Choosing Capital for Treeland (树dp)

题目链接:http://codeforces.com/contest/219/problem/D 树dp 1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio&

「CF827D Best Edge Weight」 - LCT

CF827D Best Edge Weight tags:LCT 题意 给你一张有边权的简单图,现在要求对于每一条边求出,这条边的边权最大为多少时,它还能出现在所有可能的最小生成树上,如果对于任意边权都出现,则输出 -1 题解 分类讨论 这条边在最小生成树上,那么就是所有可以取代这条边的非树边的最小值-1 这个边不在最小生成树上,那么就是这条边可以取代的树边是最小值-1 直接 LCT 维护即可,时间复杂度 \(O(n\log n)\) #include<cstdio> #include<

数据结构--树(定义与存储结构)

树基本定义 树的定义 数是具有n个节点的有限集.如图即是一个树形结构. 节点分类 节点的度:一个节点拥有的子节点即成为节点的度,比如A节点,有B和C两个子节点,那么A节点的度=2. 叶节点(终端节点):没有子节点的节点,比如G.H.I.... 如图: 节点间关系 孩子节点:某一个节点的子节点称为孩子节点.比如B.C节点是A节点的孩子节点. 双亲节点:与孩子节点相反.比如,A节点是B.C的双亲节点. 兄弟节点:同一个双亲节点的孩子节点,之间称为兄弟节点.比如,B.C为兄弟节点. 如图: 树的存储结

Codeforces 444C DZY Loves Colors(线段树)

题目大意:Codeforces 444C DZY Loves Colors 题目大意:两种操作,1是修改区间上l到r上面德值为x,2是询问l到r区间总的修改值. 解题思路:线段树模板题. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int maxn = 5*1e5; typedef long lo

CodeForces 706D Vasiliy&#39;s Multiset (字典树查询+贪心)

题意:最开始的时候有一个集合,集合里面只有一个元素0,现在有q次操作,操作分为3种: + x: 表示向集合中添加一个元素x - x:表示删除集合中值为x的一个元素 ? x:表示查询集合中与x异或的最大值为多少 析:这是一个字典树的应用,不过确实没看出来....主要思想是这样,先用10进制数,转成二进制数,记下每个结点的0,1的个数,这样增加和删除,就是对01的删除, 剩下的就是查询,那么尽量让0和1XOR是最大的,所以,对于给定数,我们要去尽量他的XOR数,如果找到就加上,找不到,就找下一个.这

Codeforces Round #261 (Div. 2) D 树状数组应用

看着题意:[1,i]中等于a[i]的个数要大于[,jn]中等于a[j]的个数 且i<j,求有多少对这样的(i,j)  ,i<j但是 i前面的合法个数 要大于j后面的 看起来很像逆序数的样子,所以很容易往树状数组想去,但是处理就看个人了,像我比赛的时候就处理得非常的麻烦,虽做出了但是花时间也多,经过杰哥的教育,其实正着塞进树状数组 反着来找就可以了,灰常的简单清晰明了,贴一发纪念我的搓比 int n; int aa[1000000 + 55]; int bb[1000000 + 55]; int

Codeforces 29D Ant on the Tree 树的遍历 dfs序

题目链接:点击打开链接 题意: 给定n个节点的树 1为根 则此时叶子节点已经确定 最后一行给出叶子节点的顺序 目标: 遍历树并输出路径,要求遍历叶子节点时按照给定叶子节点的先后顺序访问. 思路: 给每个节点加一个优先级. 把最后一个叶子节点到父节点的路径上的点优先级改为1 把倒数第二个叶子节点到父节点的路径上的点优先级改为2 如此每个点就有一个优先级,每个访问儿子节点时先访问优先级大的即可 对于无解的判断:得到的欧拉序列不满足输入的叶子节点顺序即是无解. #include <cstdio> #

数据结构--树的一些计算

先解释一下一些最基本的概念 结点的孩子结点个数即为该结点的度.度为0的结点叫叶子结点.处在树的最顶端(没有双亲)的结点叫根结点. 介绍一下公式 k:总度数k+1:总节点数 为什么总节点数肯定比总度数多1呢?其实很简单可以解释,度可以看作节点与节点之间的线,多1就是显而易见的 设该二叉树总结点数为N,叶子结点个数为n0,度为1的结点个数为n1.度为2的节点个数为n2下面可得两等式:(1) N = n2 + n0 + n1;依据:很显然,二叉树总结点数等于度分别为0,1,2的结点个数总和.(2) N