GLLF 砍木棍
GLLF 砍木棍
题目描述
上回 GLLF 对一个木头砍了 $10^{100}$ 刀,他感觉有点累了,所以他决定砍一根木棍。
他有一根长度为正整数 $n$ 的木棍,把它砍成了若干个长度为正整数的小段,他很好奇有多少种砍法能使得这些小段木棍能拼成一个凸多边形。
两种砍法不同当且仅当存在砍的位置不同。我们把每段的长度按顺序放入数组,如长度为 $6$ 的木棍按顺序砍出 $1,2,1,2$ 四段,则可以表示为 $[1,2,1,2]$。两个不同的数组代表不同的砍法。
由于这个问题的答案可能很大,请你将答案对 $10^9 + 7$ 取模之后输出。
输入描述:
第一行一个正整数 $T$ $(1 \leq T \leq 10^5)$,表示数据组数。
接下来 $T$ 行,每行一个正整数 $n$ $(3 \leq n \leq 10^9)$,表示木棍的长度。
输出描述:
对于每组数据,输出一行一个对 $10^9 + 7$ 取模之后的整数,表示有多少种砍法。
示例1
输入
5
3
4
5
114514
1919810
输出
1
1
8
669267460
159473596
备注:
对于第三个测试用例,一种有八种切法:$[1,1,1,1,1],[1,1,1,2],[1,1,2,1],[1,2,1,1],[2,1,1,1],[1,2,2],[2,1,2],[2,2,1]$
解题思路
首先要知道如下性质,一个多边形是凸多边形,要满足除最长边外其余边的总长度要比最长边严格长。因此划分的方案要满足,除最大区间和外,其余区间的和要大于最大区间和,且至少要划分出 $3$ 个区间。
发现直接求不好做,考虑能不能容斥,即先求出总的划分数量,再减去不合法的划分数量。
先考虑总的划分方案,由于我们需要把 $n$ 划分成若干段 $k \, (3 \leq k \leq n)$,且每一段的长度至少为 $1$,所有段的总和恰好等于 $n$。因此我们可以用隔板法求解,总的划分为
$$
\begin{align*}
&\sum\limits_{k=3}^{n}{C_{n-1}^{k-1}} \\
= & \left( C_{n-1}^{n-1} + C_{n-1}^{n-2} + \cdots+ C_{n-1}^{0} \right) - C_{n-1}^{1} - C_{n-1}^{0} \\
= & 2^{n-1} - n
\end{align*}
$$
当然 $2^{n-1} - n$ 也可以解释为,首先 $n-1$ 个间隔每个可以选择切割或不切割,有 $2^{n-1}$ 种方案,由于至少要有 $3$ 段划分,因此至少需要切割 $2$ 次。所以需要减去 $0$ 以及 $1$ 次切割的方案数,即 $2^{n-1} - 1 - (n-1) = 2^{n-1} - n$。
现在考虑不合法的划分方案。当除了最长边外,其余边的总和小于等于最长边,此时不合法。设最长边为 $k$,因此有 $n - k \leq k \Rightarrow k \geq \left\lceil \frac{n}{2} \right\rceil$。为此我们可以枚举所有不合法的最长边 $k \, \left(\left\lceil \frac{n}{2} \right\rceil \leq k \leq n-2\right)$,对于每种最长边 $k$ 分为两种情况。
最长边在两端。考虑在其中一端的情况,那么剩余部分至少需要划分一次,方案数为 $2^{n-k-1} - 1$。因此考虑两端的话,总的划分方案就是 $2 \left(2^{n-k-1}-1\right)$。
最长边不在两端,意味着两端剩余部分均不为空,不需要至少划分一次(即可以不划分)。枚举左边部分剩余的长度,那么总的方案数就是 $\sum\limits_{i=1}^{n-k-1}{2^{i-1} \cdot 2^{n-k-i-1}} = \sum\limits_{i=1}^{n-k-1}{2^{n-2-k}} = (n-k-1) 2^{n-2-k}$。
因此当最长边为 $k$ 时两种情况总的方案数就是 $2 \left(2^{n-k-1}-1\right) + (n-k-1) 2^{n-2-k} = (n-k+3) 2^{n-2-k} - 2$。
因此总的不合法方案数就是 $\sum\limits_{k=m}^{n-2}{\left((n-k+3) 2^{n-2-k} - 2\right)}$,记 $m = \left\lceil \frac{n}{2} \right\rceil$。事实上对于每个查询我们不可能通过枚举 $k$ 来求这个值,因此看看能否对这个求和进行化简。
把 $\sum\limits_{k=m}^{n-2}{\left((n-k+3) 2^{n-2-k} - 2\right)}$ 分成 $3$ 个部分。
$\sum\limits_{k=m}^{n-2}{-2} = -2(n-1-m)$。
$\sum\limits_{k=m}^{n-2}{(n+3)2^{n-2-k}} = (n+3)\left( 2^{n-2-m} + \cdots + 2^0 \right) = (n+3)\left( 2^{n-1-m}-1 \right)$。
以及 $-\sum\limits_{k=m}^{n-2}{k \cdot 2^{n-2-k}}$。该式可通过错位相减法进行化简。
记 $s = \sum\limits_{k=m}^{n-2}{k \cdot 2^{n-2-k}}$,$2s = \sum\limits_{k=m}^{n-2}{k \cdot 2^{n-1-k}} = k \cdot 2^{n-1-m} + \sum\limits_{k=m+1}^{n-2}{k \cdot 2^{n-1-k}} = k \cdot 2^{n-1-m} + \sum\limits_{k=m}^{n-3}{(k+1) 2^{n-2-k}}$。因此
$$
\begin{align*}
&2s - s \\
&= s \\
&= k \cdot 2^{n-1-m} + \sum\limits_{k=m}^{n-3}{(k+1) 2^{n-2-k}} - \left( \sum\limits_{k=m}^{n-3}{k \cdot 2^{n-2-k}} + (n-2)2^{0} \right) \\
&= k \cdot 2^{n-1-m} + \sum\limits_{k=m}^{n-3}{2^{n-2-k}} - n + 2 \\
&= k \cdot 2^{n-1-m} + \left( 2^{n-2-m} + 2^{n-1-m} + \cdots + 2^{1} + 2^0 \right) - 2^0 - n + 2 \\
&= k \cdot 2^{n-1-m} + 2^{n-1-m} - 1 - 1 - n + 2 \\
& = k \cdot 2^{n-1-m} + 2^{n-1-m} - n
\end{align*}
$$
因此 $-\sum\limits_{k=m}^{n-2}{k \cdot 2^{n-2-k}} = -k \cdot 2^{n-1-m} - 2^{n-1-m} + n$。
因此 $\sum\limits_{k=m}^{n-2}{\left((n-k+3) 2^{n-2-k} - 2\right)} = -2(n-1-m) + (n+3)\left( 2^{n-1-m}-1 \right) - k \cdot 2^{n-1-m} - 2^{n-1-m} + n$。
因此总的合法方案数就是 $2^{n-1} - n - \sum\limits_{k=m}^{n-2}{\left((n-k+3) 2^{n-2-k} - 2\right)} = 2^{n-1} - (n-m+2)2^{n-1-m} + (n - 2m + 1)$。
AC 代码如下,时间复杂度为 $O(T \log{n})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
int qmi(int a, int k) {
int ret = 1;
while (k) {
if (k & 1) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod;
k >>= 1;
}
return ret;
}
void solve() {
int n;
cin >> n;
int m = n + 1 >> 1;
cout << ((qmi(2, n - 1) + (n - 2 * m + 1) - (n - m + 2ll) * qmi(2, n - 1 - m)) % mod + mod) % mod << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
参考资料
【题解】“华为杯” 2024年广东工业大学新生赛(同步赛):https://ac.nowcoder.com/discuss/1442965
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18588761