CF1761D
考虑设 f(i) 表示从低到高第 i 位的进位情况,0 表示不进位,1 表示进位。
分类讨论一下:
- f(i−1)=f(i)=1,那么 a,b 这一位有三种情况 (1,1),(1,0),(0,1)。
- f(i−1)=f(i)=0,那么 a,b 这一位有三种情况 (0,0),(1,0),(0,1)。
- f(i−1)=1,f(i)=0,那么 a,b 这一位有一种情况 (0,0)。
- f(i−1)=0,f(i)=1,那么 a,b 这一位有一种情况 (1,1)。
于是发现 f(i)=f(i−1) 要乘 3,否则不变。
设总共有 n+1 位,并强制 f(0)=0。
枚举有 x 个位置满足 f(i)≠f(i−1),那么总共有 x+1 个 01 连续段,其中 0 的连续段有 ⌈x+12⌉ 个,1 的连续段有 ⌊x+12⌋ 个。
由于要填 k 个 1,所以要填 n+1−k 个 0。
而把 x 个元素分成 y 段的方案数,经典的隔板法,是 (x−1y−1)。
于是总的式子是
n∑i=13n−i×((n+1−k)−1⌈i+12⌉−1)×(k−1⌊i+12⌋−1)
记得特判 k=0 的情况。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000005, mod = 1e9 + 7;
int n, k;
int fac[N], inv[N], pw[N];
int ans;
int qpow(int x, int y) {
int res = 1;
while (y) {
if (y & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod;
y >>= 1;
}
return res;
}
void init(int n) {
fac[0] = pw[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod, pw[i] = 1ll * pw[i - 1] * 3 % mod;
inv[n] = qpow(fac[n], mod - 2);
for (int i = n - 1; ~i; --i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
}
int C(int n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
return 1ll * fac[n] * inv[n - m] % mod * inv[m] % mod;
}
void add(int &a, int b) {
a += b;
if (a >= mod) a -= mod;
}
int main() {
scanf("%d%d", &n, &k);
init(n);
if (!k) return printf("%d", pw[n]), 0;
for (int i = 1; i <= n; ++i) add(ans, 1ll * pw[n - i] * C(n + 1 - k - 1, ((i + 1) - (i + 1) / 2) - 1) % mod * C(k - 1, (i + 1) / 2 - 1) % mod);
printf("%d", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通