1 条题解
-
0
给每一列赋予两种属性:
- 会不会往下一位进位:。
- 需不需要上一位进位:$b_i = \left[x_i + y_i + 1 \equiv z_i \pmod {10}\right]$。
发现 的表述还不是很完备,修改为 。
所有列转化为 的形式。
我们发现一旦一个数的第二位为 ,其右边与之相邻的必然满足第一位为 。
同理如果一个数的第一位为 ,其左边与之相邻的必然满足第二位为 。
最后的答案一定形如 $\texttt{00}\ \texttt{01}\ \texttt{11}\cdots\texttt{11}\ \texttt{10} \ \texttt{00}\cdots$,即:
- 和 数量相等,且两两配对。
- 只能加在一对 中间。
- 只能加在开头,结尾,或一对 当中。
到此步为止,已经容易计数不考虑前导零的方案数了。
考虑容斥,钦定开头是 或 ,满足 ,剩下的部分也容易计数。
#include<bits/stdc++.h> #define eb emplace_back #define ep emplace using namespace std; using ll = long long; constexpr int N = 4e5 + 5, mod = 1e9 + 7; int n, cnt[2][2][2]; char a[N], b[N], c[N]; ll fac[N], inv[N]; int s(int i, int j) { return cnt[i][j][0] + cnt[i][j][1]; } ll qpow(ll a, int b = mod - 2) { ll c = 1; while(b) { if(b & 1) c = c * a % mod; b >>= 1; a = a * a % mod; } return c; } ll up(int i, int k) { return fac[i + k - 1] * inv[i - 1] % mod; } ll C(int i, int j) { if(j < 0) return 1; return fac[i] * inv[j] % mod * inv[i - j] % mod; } int main() { scanf("%s%s%s", a + 1, b + 1, c + 1); n = strlen(a + 1); for(int i = 1; i <= n; ++ i) { int x = a[i] - '0', y = b[i] - '0', z = c[i] - '0'; int o = 0; if((x + y + 1) % 10 == z) o = 1; else if((x + y) % 10 != z) return cout << 0, 0; ++ cnt[x + y + o >= 10][o][x && y && z]; } if(s(0, 1) != s(1, 0) || s(1, 1) && !s(0, 1)) { return cout << 0, 0; } fac[0] = 1; for(int i = 1; i <= 2 * n; ++ i) fac[i] = i * fac[i - 1] % mod; inv[2 * n] = qpow(fac[2 * n]); for(int i = 2 * n; i >= 1; -- i) inv[i - 1] = inv[i] * i % mod; ll ans = fac[s(0, 1)] * fac[s(0, 1)] % mod; ll coef = C(s(1, 1) + s(0, 1) - 1, s(0, 1) - 1) * fac[s(1, 1)] % mod; ans = ans * coef % mod; ll tmp = ans; ans = ans * up(s(0, 1) + 1, s(0, 0)) % mod; /* 减去前导0 */ ans = (ans + mod - tmp * cnt[0][0][0] % mod * up(s(0, 1) + 1, s(0, 0) - 1) % mod) % mod; tmp = fac[s(1, 0)] * fac[s(0, 1) - 1] % mod * cnt[0][1][0] % mod; tmp = tmp * coef % mod * up(s(0, 1), s(0, 0)) % mod; cout << (ans + mod - tmp) % mod; return 0; }
- 1
信息
- ID
- 10283
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者