组合数学
HDU6333
组询问,每次询问 。
。
设 ,画个杨辉三角易得
莫队即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read() {
int x = 0, f = 0; char c = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + (c & 15), c = getchar();
return f ? -x : x;
}
#define N 100005
#define P 1000000007
#define iv2 500000004
int fac[N], inv[N], n, res[N];
struct node { int n, m, i; } a[N];
int qow(int x, int k, int r = 1) {
for (; k; k >>= 1, x = x * x % P)
if (k & 1) r = r * x % P;
return r;
}
int C(int n, int m) {
if (!n || !m) return 1; if (m > n) return 0;
return fac[n] * inv[m] % P * inv[n - m] % P;
}
signed main() {
fac[0] = 1;
for (int i = 1; i < N; i ++) {
fac[i] = fac[i - 1] * i % P;
}
inv[N - 1] = qow(fac[N - 1], P - 2);
for (int i = N - 2; ~i; i --) {
inv[i] = inv[i + 1] * (i + 1) % P;
}
n = read();
for (int i = 1; i <= n; i ++) {
a[i].n = read(), a[i].m = read(), a[i].i = i;
}
sort(a + 1, a + n + 1, [](node a, node b) {
return a.m < b.m;
});
int B = sqrt(n);
for (int l = 1, r = B; l <= n;) {
sort(a + l, a + r + 1, [](node a, node b) {
return a.n < b.n;
});
l = r + 1, r = min(n, l + B - 1);
}
int tn = a[1].n, tm = 0ll, ans = 1;
for (int i = 1; i <= n; i ++) {
while (tm < a[i].m) (ans += C(tn, ++ tm)) %= P;
while (tm > a[i].m) (ans += P - C(tn, tm --)) %= P;
while (tn < a[i].n) ans = (ans * 2 + P - C(tn ++, tm)) % P;
while (tn > a[i].n) ans = ((ans + C(-- tn, tm)) * iv2 % P) % P;
res[a[i].i] = ans;
}
for (int i = 1; i <= n; i ++) {
printf("%lld\n", res[i]);
}
return 0;
}
/*
F(n, m) = C(n, 0) +...+ C(n, m);
F(n, m + 1) = F(n, m) + C(n,m + 1);
F(n + 1, m) = 2F(n, m) - C(n, m);
*/HDU7060
给定 位数,求所有把 分割至多成 段的方案中所有段数字的和(可以有前导零)。
对 取模。
。
考虑每个位置 的贡献。如果 在数字的第 位( 从 开始),那么贡献就是 ,我们还需要计算此时的方案数。
用隔板法考虑位置,分类讨论:
- ,此时已选一个位置,那么就是在 个位置中选最多 个,;
- ,此时已选两个位置,那么就是在 个位置中选最多 个,;
贡献与 无关,对于每个 统计 的数量,答案就是:
发现就是上题的 ,且 中的 只有 ,直接预处理即可做到 。
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read() {
int x = 0, f = 0; char c = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + (c & 15), c = getchar();
return f ? -x : x;
}
#define P 998244353
#define N 1000005
int fac[N], inv[N], k, a[N], n, f[N], g[N];
char s[N];
int qow(int x, int k, int r = 1) {
for (; k; k >>= 1, x = x * x % P)
if (k & 1) r = r * x % P;
return r;
}
int C(int n, int m) {
if (!n || !m) return 1; if (m > n) return 0;
return fac[n] * inv[m] % P * inv[n - m] % P;
}
signed main() {
fac[0] = 1;
for (int i = 1; i < N; i ++) {
fac[i] = fac[i - 1] * i % P;
}
inv[N - 1] = qow(fac[N - 1], P - 2);
for (int i = N - 2; ~i; i --) {
inv[i] = inv[i + 1] * (i + 1) % P;
}
for (int T = read(); T --;) {
k = read(), scanf("%s", s + 1);
n = strlen(s + 1);
for (int i = 1; i <= n; i ++) {
a[i] = a[i - 1] + (s[i] & 15);
}
f[0] = g[0] = 1;
for (int i = 1; i <= k - 2; i ++) {
f[i] = (f[i - 1] + C(k - 2, i)) % P;
g[i] = (g[i - 1] + C(k - 1, i)) % P;
} g[k - 1] = (g[k - 2] + 1) % P;
for (int i = max(1ll, k - 1); i <= n; i ++) { if (i >= k)
g[i] = (2 * g[i - 1] + P - C(i - 1, k - 1)) % P;
f[i] = (2 * f[i - 1] + P - C(i - 1, k - 2)) % P;
}
int res = 0;
for (int j = 0; j < n; j ++) {
int J = qow(10, j), x = 0;
if (n - j - 2 >= 0 && k - 2 >= 0) {
if (n - j - 2 < k - 2) x = qow(2, n - j - 2);
else x = f[n - j - 2];
res = (res + a[n - j - 1] * J % P * x % P) % P;
}
if (n - j - 1 < k - 1) x = qow(2, n - j - 1);
else x = g[n - j - 1];
res = (res + (s[n - j] & 15) * J % P * x % P) % P;
}
printf("%lld\n", res);
}
return 0;
}
/*
F(n, m) = C(n, 0) +...+ C(n, m);
F(n, m + 1) = F(n, m) + C(n,m + 1);
F(n + 1, m) = 2F(n, m) - C(n, m);
*/CF1716F
设一个长度为 ,元素取值在 内的数组 中奇数元素个数为 ,则令 。
对所有可能的 ,求 。
对 取模。
;;
设 中奇数个数为 ,偶数为 ,枚举奇数个数,则答案为:
这个东西怎么算呢。。。。
考虑 的证明过程:
对两边同时求导:
取 即得证。
拓展一下:
右边这个和我们要求的很像,但是少了个 ,我们把 看成常数求个导再乘上 ,就变成了我们想要的东西:
但这只是 的情况。我们可以通过求 阶导并乘 得到答案,例如 :
观察发现,答案形如 ,其中 是一个我们还不知道的系数。
考虑求出 的递推式,那么对 导:
然后发现 , 是斯特林数。别问我怎么看出来的然后预处理就可以做到 了。
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read() {
int x = 0, f = 0; char c = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + (c & 15), c = getchar();
return f ? -x : x;
}
#define N 2005
#define P 998244353
int n, m, k, s[N][N];
int qow(int x, int k, int r = 1) {
for (; k; k >>= 1, x = x * x % P) {
if (k & 1) r = r * x % P;
}
return r;
}
signed main() {
s[1][1] = 1;
for (int i = 2; i < N; i ++) {
for (int j = 1; j <= i; j ++) {
s[i][j] = (s[i - 1][j - 1] + j * s[i - 1][j]) % P;
}
}
for (int T = read(); T --;) {
n = read(), m = read(), k = read(); int res = 0, x = (m + 1) / 2, inv = qow(m, P - 2);
for (int i = 1, K = x * qow(m, n - 1) % P * n % P; i <= min(n, k); i ++, K = K * x % P * inv % P * (n - i + 1) % P) res = (res + s[k][i] * K) % P;
printf("%lld\n", res);
}
return 0;
}ARC137D
求对于长度为 的序列 对于每个 求进行 次操作后的 。
一次操作为令 。
,。
将每次操作后的 写成一行排成一个矩阵 ,考虑 对 的贡献。
经典问题,贡献是 ,因为是异或考虑这个东西的奇偶性。
Lucas 定理的推论: 是奇数当且仅当 是 的二进制子集。
因为 是 的二进制子集,那么 和 在二进制下是互斥的。高维前缀和即可。
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 0; char c = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + (c & 15), c = getchar();
return f ? -x : x;
}
#define M 21
#define N (1 << 21)
int n, m, a[N], f[N];
int main() {
n = read(), m = read();
for (int i = 1; i <= n; i ++) {
a[i] = read(), f[n - i] ^= a[i];
}
for (int i = 0; i <= 20; i ++) {
for (int j = 0; j < 1 << 20; j ++) {
if (j >> i & 1) f[j] ^= f[j ^ (1 << i)];
}
}
for (int i = 0; i < m; i ++) {
printf("%d ", f[((1 << 20) - 1) ^ i]);
}
puts("");
return 0;
}组合数学
https://ybwa.github.io/p/7ff86023/