k-size字符串
链接:https://ac.nowcoder.com/acm/contest/5600/B
题解:https://ac.nowcoder.com/discuss/428377
题目描述
牛妹最近在研究k-size字符串。
一个字符串为k-size指,字符串的连续段共有
个。所谓连续段指尽可能多的相同连续字母组成的子串。
例如:aabbbccc为3-size,因为(‘aa’ ‘bb’ ‘ccc’),ababaab为6-size,因为 (‘a’ ‘b’ ‘a’ ‘b’ ‘aa’ ‘b’)。
牛妹想知道,由
个 ‘a’ 字符,
个 ‘b’ 字符,组成长度为
的k-size字符串,共有多少种组成方式?由于该数可能过大,请对
取模。
输入描述:
三个正整数
和
,用空格隔开。
输出描述:
一个正整数,为方案数对
取模的结果。
示例1
输入
2 2 2
输出
2
示例2
输入
1 2 3
输出
1
思路:
只有两种不同的元素
、
来组成
段,那么最后的结果肯定是
不同的段交叉。
是偶数时,先拿出 个 和 个 摆好: ,然后剩下的 和 就是往已经摆好的位置上插。
这是高中排列组合中的一个问题:有
个相同元素分成
组,每组有非负整数个元素。
解法:
个元素和劈
刀,共
个位置,在这些位置上选
个位置放上元素,那么剩余位置就相当于是劈了
刀,分成了
组,每组都是大于等于零个,方案数即
。k是偶数时就是
个元素分成
组。
是奇数时,与上面类似,先拿出 个 和 个 摆好: ,然后剩下的往里插。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll power(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
res = res * a % mod;
b >>= 1, a = a * a % mod;
}
return res % mod;
}
ll inv(ll x) { return power(x, mod - 2); }
ll C(ll n, ll m)
{
ll res = 1;
for (int i = 1; i <= m; i++)
res = res * (n - i + 1) % mod * inv(i) % mod;
return res;
}
ll f(ll x, ll y) //x个相同元素分成y个非负整数的组的方案数
{
if (x < 0 || y <= 0)
return 0;
return C(x + y - 1, x);
}
int main()
{
ll n, m, k;
cin >> n >> m >> k;
ll ans = 0;
if (k & 1)
ans = (f(n - k / 2, k / 2) * f(m - k / 2 - 1, k / 2 + 1) + f(n - k / 2 - 1, k / 2 + 1) * f(m - k / 2, k / 2)) % mod;
else
ans = 2LL * f(n - k / 2, k / 2) * f(m - k / 2, k / 2) % mod;
cout << ans << endl;
return 0;
}