[CF] CF156C Cipher

Description

Link

Solution

az看成126。注意到不断把前一个数+1,后一个数1或者把前一个数1,后一个数+1,它们的和总是不变。且对于每个和相等的数列,我们总能通过若干次上述操作把它们转化成一样的。从而,问题就变成了n126的数和为s的方案。

首先有O(n2262)dp:设dp[i][j]表示前i个数和为j的方案数。那么dp[i][j]dp[i1][jk]

然后有O(n)的容斥(O(26n)预处理)

如果没有每个数[1,26]的限制,答案就直接是(s1n1)(插板法)

现在有了这个限制,考虑容斥:我们钦定给i个数预先分配26,那么至少有i个数不合法,对答案的贡献就为(1)i(总方案数减去至少有一个不合法的,加上至少有两个不合法的。。。),相当于给i个数预先分配26再做正常的不定方程求解。

故答案为

i=0n(1)i(ni)(s26i1n1)

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;
}
posted @   andysj  阅读(72)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示