GLLF 砍木棍

GLLF 砍木棍

题目描述

上回 GLLF 对一个木头砍了 10100 刀,他感觉有点累了,所以他决定砍一根木棍。

他有一根长度为正整数 n 的木棍,把它砍成了若干个长度为正整数的小段,他很好奇有多少种砍法能使得这些小段木棍能拼成一个凸多边形。

两种砍法不同当且仅当存在砍的位置不同。我们把每段的长度按顺序放入数组,如长度为 6 的木棍按顺序砍出 1,2,1,2 四段,则可以表示为 [1,2,1,2]。两个不同的数组代表不同的砍法。

由于这个问题的答案可能很大,请你将答案对 109+7 取模之后输出。

输入描述:

第一行一个正整数 T (1T105),表示数据组数。

接下来 T 行,每行一个正整数 n (3n109),表示木棍的长度。

输出描述:

对于每组数据,输出一行一个对 109+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(3kn),且每一段的长度至少为 1,所有段的总和恰好等于 n。因此我们可以用隔板法求解,总的划分为
k=3nCn1k1=(Cn1n1+Cn1n2++Cn10)Cn11Cn10=2n1n
  当然 2n1n 也可以解释为,首先 n1 个间隔每个可以选择切割或不切割,有 2n1 种方案,由于至少要有 3 段划分,因此至少需要切割 2 次。所以需要减去 0 以及 1 次切割的方案数,即 2n11(n1)=2n1n

  现在考虑不合法的划分方案。当除了最长边外,其余边的总和小于等于最长边,此时不合法。设最长边为 k,因此有 nkkkn2。为此我们可以枚举所有不合法的最长边 k(n2kn2),对于每种最长边 k 分为两种情况。

  最长边在两端。考虑在其中一端的情况,那么剩余部分至少需要划分一次,方案数为 2nk11。因此考虑两端的话,总的划分方案就是 2(2nk11)

  最长边不在两端,意味着两端剩余部分均不为空,不需要至少划分一次(即可以不划分)。枚举左边部分剩余的长度,那么总的方案数就是 i=1nk12i12nki1=i=1nk12n2k=(nk1)2n2k

  因此当最长边为 k 时两种情况总的方案数就是 2(2nk11)+(nk1)2n2k=(nk+3)2n2k2

  因此总的不合法方案数就是 k=mn2((nk+3)2n2k2),记 m=n2。事实上对于每个查询我们不可能通过枚举 k 来求这个值,因此看看能否对这个求和进行化简。

  把 k=mn2((nk+3)2n2k2) 分成 3 个部分。

  k=mn22=2(n1m)

  k=mn2(n+3)2n2k=(n+3)(2n2m++20)=(n+3)(2n1m1)

  以及 k=mn2k2n2k。该式可通过错位相减法进行化简。

  记 s=k=mn2k2n2k2s=k=mn2k2n1k=k2n1m+k=m+1n2k2n1k=k2n1m+k=mn3(k+1)2n2k。因此
2ss=s=k2n1m+k=mn3(k+1)2n2k(k=mn3k2n2k+(n2)20)=k2n1m+k=mn32n2kn+2=k2n1m+(2n2m+2n1m++21+20)20n+2=k2n1m+2n1m11n+2=k2n1m+2n1mn
  因此 k=mn2k2n2k=k2n1m2n1m+n

  因此 k=mn2((nk+3)2n2k2)=2(n1m)+(n+3)(2n1m1)k2n1m2n1m+n

  因此总的合法方案数就是 2n1nk=mn2((nk+3)2n2k2)=2n1(nm+2)2n1m+(n2m+1)

  AC 代码如下,时间复杂度为 O(Tlogn)

#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

posted @   onlyblues  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示