[CF] CF156C Cipher
Description
Solution
把\(a\sim{z}\)看成\(1\sim26\)。注意到不断把前一个数\(+1\),后一个数\(-1\)或者把前一个数\(-1\),后一个数\(+1\),它们的和总是不变。且对于每个和相等的数列,我们总能通过若干次上述操作把它们转化成一样的。从而,问题就变成了\(n\)个\(1\sim26\)的数和为\(s\)的方案。
首先有\(O(n^226^2)\)的\(dp\):设\(dp[i][j]\)表示前\(i\)个数和为\(j\)的方案数。那么\(dp[i][j]\leftarrow{dp[i-1][j-k]}\)
然后有\(O(n)\)的容斥(\(O(26n)\)预处理)
如果没有每个数\(\in[1,26]\)的限制,答案就直接是\(\dbinom{s-1}{n-1}\)(插板法)
现在有了这个限制,考虑容斥:我们钦定给\(i\)个数预先分配\(26\),那么至少有\(i\)个数不合法,对答案的贡献就为\((-1)^i\)(总方案数减去至少有一个不合法的,加上至少有两个不合法的。。。),相当于给\(i\)个数预先分配\(26\)再做正常的不定方程求解。
故答案为
\[\sum\limits_{i=0}^n(-1)^i\dbinom{n}{i}\dbinom{s-26i-1}{n-1}
\]
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int lim = 2600;
const ll mod = 1e9 + 7;
int t, n;
char ch[105];
ll fac[2605], inv[2605];
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
return x * fl;
}
ll qpow(ll base, ll pw)
{
ll s = 1;
while (pw)
{
if (pw & 1) s = s * base % mod;
base = base * base % mod;
pw >>= 1;
}
return s;
}
ll C(ll x, ll y)
{
if (x < y) return 0;
return fac[x] * inv[y] % mod * inv[x - y] % mod;
}
int main()
{
fac[0] = 1;
for (int i = 1; i <= lim; i ++ ) fac[i] = fac[i - 1] * (ll)(i) % mod;
inv[lim] = qpow(fac[lim], mod - 2);
for (int i = lim - 1; i >= 0; i -- ) inv[i] = inv[i + 1] * (ll)(i + 1) % mod;
t = read();
while (t -- )
{
scanf("%s", ch + 1);
n = strlen(ch + 1);
int sum = 0;
for (int i = 1; i <= n; i ++ ) sum += ch[i] - 'a' + 1;
ll res = -1;
for (int i = 0, o = 1; i <= n; i ++ , o *= -1)
res = (res + 1ll * o * C(n, i) % mod * C(sum - 26 * i - 1, n - 1) % mod + mod) % mod;
printf("%lld\n", res);
}
return 0;
}