题目链接
题目描述
给定 个顶点,你可以用这些顶点构成一棵树。
如果这棵树恰好只有三个叶子节点(度为1的节点),我们称之为“Y型树”。
请问,用这 个顶点可以构建出多少种不同的Y型树?
结果需要对 取模。
解题思路
1. 问题解读:有标号 vs. 无标号
虽然题目提到了“顶点”,通常在图论中暗示顶点是带标号的,但题目的样例揭示了问题的本质。
- 对于
,如果顶点有标号,我们可以任选一个点作为中心(度为3),剩下3个点作为叶子。这样有
种不同的树。但这与样例输出“1”矛盾。
- 对于
,样例输出“2”。
这说明题目实际上是在问,有多少种结构上不同(同构)的Y型树,即这是一个无标号图的计数问题。
2. Y型树的结构与整数划分
- 一棵有3个叶子的树,其结构必然是由一个度为3的中心节点,连接着三条“臂”(链)构成。
- 由于我们不关心顶点的标号,只关心树的形态,那么唯一决定Y型树结构的因素就是这三条臂的长度。
个顶点中,1个作为中心,剩下
个顶点分布在这三条臂上。设三条臂的顶点数分别为
。
- 我们必须有
,且
(因为每条臂至少要有一个顶点,即叶子节点)。
因此,问题被完美地转化为了一个经典的组合数学问题:将整数 划分为 3 个正整数之和,有多少种不同的划分方案?
3. 整数划分公式
- 一个整数
划分为 3 个正整数之和的方案数,等价于将
划分为最多 3 个非负整数之和的方案数。
- 对于将整数
划分为最多3个部分,有一个著名的近似公式,其精确值为
。
- 在本题中,我们要划分的是
,所以对应的
是
。
- 代入公式,方案数为
。
- 为了避免浮点数计算,我们可以使用等价的整数算式:
,即
((n-1)^2 + 6) / 12
。
4. 大数处理
的范围可达
,所以
会溢出标准的64位整型。需要使用
__int128
(C++),BigInteger
(Java) 或 Python 的原生大数支持。- 计算出划分数(一个整数)之后,再对这个结果取模
。注意,这里是常规除法,不是模逆元。
代码
#include <bits/stdc++.h>
using namespace std;
// 使用__int128处理大数
// 注意:该类型在某些编译器或在线判题系统中可能不支持
// 在本地GCC/Clang通常是支持的
// 自定义输出函数,因为标准cout不支持__int128
ostream& operator<<(ostream& os, __int128_t value) {
if (value == 0) return os << "0";
string s = "";
bool neg = value < 0;
if (neg) value = -value;
while (value > 0) {
s += (char)(value % 10 + '0');
value /= 10;
}
if(neg) s += '-';
reverse(s.begin(), s.end());
return os << s;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
long long n_ll;
cin >> n_ll;
if (n_ll < 4) {
cout << 0 << endl;
return 0;
}
__int128_t n = n_ll;
__int128_t term = n - 1;
__int128_t result = (term * term + 6) / 12;
long long mod = 1e9 + 7;
cout << (long long)(result % mod) << endl;
return 0;
}
import java.util.Scanner;
import java.math.BigInteger;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
BigInteger n = sc.nextBigInteger();
BigInteger four = new BigInteger("4");
if (n.compareTo(four) < 0) {
System.out.println(0);
return;
}
BigInteger term = n.subtract(BigInteger.ONE);
BigInteger termSquared = term.multiply(term);
BigInteger numerator = termSquared.add(new BigInteger("6"));
BigInteger denominator = new BigInteger("12");
BigInteger result = numerator.divide(denominator);
BigInteger mod = new BigInteger("1000000007");
System.out.println(result.mod(mod));
}
}
import sys
def solve():
n_str = sys.stdin.readline()
if not n_str: return
n = int(n_str)
if n < 4:
print(0)
return
term = n - 1
result = (term * term + 6) // 12
mod = 10**9 + 7
print(result % mod)
solve()
算法及复杂度
-
算法:数学 / 整数划分
-
时间复杂度:
。计算过程只涉及几次大数运算,其时间复杂度与
的位数(即
)有关,但通常视为常数时间。
-
空间复杂度:
。