[CF1338B] Edge Weight Assignment
题目描述
简化描述:给定一棵 \(n\) 个点的无根树,要求给每条边分配一个正整数权值,使得任意两个叶子节点之间路径上的边权异或值为 \(0\)。求最少要多少种不同权值,以及最多可以使用多少种不同权值。填入的边权值可以为任意大,\(3\leq n\leq 10^5\)。
解题思路
求解最少
如果任意两个叶子节点之间都是偶数条边,那么我们只需要把所有边填上同一个值即可。如果出现了奇数条边的路径呢?
这里我们需要几个命题:
我们先将两个节点 \(u\) 和 \(v\) 之间路径的边数记为 \(D\{u,v\}\),叶子节点集合记为 \(\mathbb{L}\),而根节点记为 \(R\)。
命题一:对于一棵节点数 \(n\geq 3\) 的树,\(D\{u,v\}\geq 2\),\(u,v\in\mathbb{L}\)。
- 证明:如果 \(\exists D\{u,v\}=1\),\(u,v\in\mathbb{L}\),结合 \(\mathbb{L}\) 的节点度数为 \(1\) 得知这棵树节点只有 \(2\) 个,导出矛盾。
命题二:如果 \(D\{u,v\}=2k+3\),\(u,v\in\mathbb{L}\),\(k\in\mathbb{N}\),那么使这些边权值异或为 \(0\) 的不同权值个数最小为 \(3\)。
- 证明:将 \(2k+1\) 条边的权值设置为相同权值,对余下 \(2\) 条边根据异或关系分配不同的权值即可。如若给 \(2k+2\) 条边分配相同权值,则最终异或值必为余下边的权值大小,而不等于 \(0\)。
考虑好了两叶子节点的路径(可以称为链),那么我们就需要考虑多链相交的情况了。
命题三:\(D\{u,v\}\) 和 \(D\{R,u\}+D\{R,v\}\) 奇偶性相同。
- 证明:假设 \(u,v\) 的最近公共祖先节点为 \(A_{u,v}\),则有 \(D\{R,u\}+D\{R,v\}=2D\{R,A_{u,v}\}+D\{A_{u,v},u\}+D\{A_{u,v},v\}\)。不难知道 \(D\{A_{u,v},u\}+D\{A_{u,v},v\}=D\{u,v\}\),而 \(2D\{R,A_{u,v}\}\) 恒为偶数,所以 \(D\{u,v\}\) 和 \(D\{R,u\}+D\{R,v\}\) 奇偶性相同。
答案:如果 \(D\{R,u\}\) 均为偶数,\(u\in\mathbb{L}\),则答案为 \(1\),否则答案为 \(3\)。
- 证明:根据命题三,如果满足均为偶数的条件,那么必然有 \(D\{u,v\}\) 均为偶数,\(u,v\in\mathbb{L}\)。不然,根据命题二,答案至少为 \(3\)。下面证明答案可以取到 \(3\):对于 \(R,u\in\mathbb{L}\),考虑命题二中余下的 \(2\) 个不同权值,将它们分别分配到与 \(R,u\) 相连的边。再考虑任意 \(v\in\mathbb{L}\),如果 \(D\{R,v\}\) 为偶数,那么与 \(v\) 相连的边权等于与 \(R\) 相连的,根据命题三,此时 \(D\{u,v\}\) 为奇数,满足此构造;如果 \(D\{R,v\}\) 为奇数,那么与 \(v\) 相连的边权等于与 \(u\) 相连的,根据命题三,此时 \(D\{u,v\}\) 为偶数,满足此构造。于是我们就找到了答案为 \(3\) 的一种构造,结论成立。
那么,如何编程来寻找奇数的 \(D\) 呢?我们需要确定好 \(R\in\mathbb{L}\),如果存在 \(u,v\in\mathbb{L}\),其 \(D\{u,v\}\) 为奇数,那么根据命题三,\(D\{R,u\},D\{R,v\}\) 必有一个奇数。所以只需一次 DFS 遍历,以 \(R\) 节点为深度 \(0\),寻找是否存在奇数深度叶子节点即可。
求解最多
同样的,这里我们也需要几个命题:
我们记异或运算符为:\(\otimes\),记一组数 \(a_i\) 的异或和为 \(\prod_{\otimes} a\),记 \(u,v\in\mathbb{L}\) 之间边权值异或和为 \(\prod_{\otimes}\{u,v\}\)。
命题四:给定数 \(t\) 可以找出无数种不同的一组数 \(a_i\)(\(|a|\geq 2\)),使得 \(\prod_{\otimes} a=t\)。
- 证明:从二进制角度考虑,保证 \(t\) 对应的二进制位最后异或结束依然存在,而其它各位最后异或结束后被消去即可。由于所选数没有上限,这样的可能有无数种。如果 \(|a|=1\) 则只有 \(a_1 = t\) 一种可能。
那么根据命题四,我们在任意节点 \(u\) 时,如果其儿子中有 \(x\) 个非叶子节点和 \(y\) 个叶子节点,那么可以新产生的不同的数的个数为 \(x+[y>0]\),因为其所有叶子节点的值都必须相等。将这些贡献累加得到答案。特别的,如果根节点也是叶子节点,那么必须判断其儿子是否有叶子节点,如果有,则答案应减少 \(1\)(上述流程操作完毕后)。
代码一览
#include <bits/stdc++.h>
using namespace std;
vector<int> G[100005];
int n, l, r;
int leaves;
int rtnd;
bool have_leaves[100005];
int f;
bool jishu;
bool vis[100005];
bool dfs(int u, int dpth)
{
int s = G[u].size();
if(s == 1 && vis[G[u][0]])
{
if(dpth&1)
jishu = true;
leaves += 1;
return true;
}
bool haveleaf = false;
vis[u] = true;
for(register int i = 0 ; i < s; i += 1)
{
int v = G[u][i];
if(vis[v])
continue;
if(dfs(v, dpth+1))
{
if(!haveleaf)
f += 1, haveleaf = true;
}
else
{
if(u != rtnd)
f += 1;
else if(!have_leaves[v])
f += 1;
}
}
if(haveleaf)
have_leaves[u] = true;
return false;
}
int main()
{
register int i;
cin >> n;
for(i = 1; i < n; i += 1)
{
cin >> l >> r;
G[l].push_back(r);
G[r].push_back(l);
}
for(rtnd = 1; rtnd <= n; rtnd += 1)
if(G[rtnd].size() == 1)
{
dfs(rtnd, 0);
leaves += 1;
break;
}
cout << (jishu?3:1) << ' ' << f;
return 0;
}