学期

Description

长度为 \(n\) 的序列, \(1/2\)个-1 , \(1/2\)个+1 ,问存在一个前缀值\(-k\)的方案数有多少.

Solution

K=1显然是Catalan数, 考虑Catalan数的证明过程
若数列不合法,则一定存在一个位置x,在这之前有m个+1,m + k个-1.
我们把之后的1变为-1,-1变为1.
这个东西好像叫反射
则该序列含有N + K个-1, N个1
方案数为\(2n\choose n + k\)
用总的\(2n\choose n\)减去即可.

因为错误的公式挂了40分.

Code

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
const int N = 1005;
const int M = 2e6 + 5;
long long mod;
int f[N << 1][N];

int pri[M], tot;
bool mr[M];
void Generate() {
	mr[1] = 1;
	for (int i = 2; i < M; i += 1) {
		if (not mr[i])
			pri[++tot] = i;
		for (int j = 1; j <= tot and i * pri[j] < M; j += 1) {
			mr[i * pri[j]] = true;
			if (i % pri[j] == 0) break;
		}
	}
}
inline long long Pow(long long a, long long b) {
	long long res = 1, base = a;
	while (b) {
		if (b & 1) res = res * base % mod;
		base = base * base % mod;
		b >>= 1;
	}
	return res;
}
inline long long Fen(int n, int p) {
	long long res = 0;
	while (n) { res += n / p, n /= p; }
	return res;
}

long long Fenjie(int n, int k) {
	long long A = 1, B = 1;
	long long p;
	for (int i = 1; i <= tot; i += 1) {
		p = Fen(2 * n, pri[i]);
		if (not p) continue;
		A = A * Pow(pri[i], p - 2ll * Fen(n, pri[i])) % mod;
		B = B * Pow(pri[i], p - Fen(n + k, pri[i]) - Fen(n - k, pri[i])) % mod;
	}
	return ((A - B) % mod + mod) % mod;
}

int main () {
	Generate();
	int n, k;
	scanf("%d%d%d", &n, &k, &mod);
	if (n > 1000) {
		printf("%I64d\n", Fenjie(n, k));
		return 0;
	}
	int nn = n << 1;
	f[0][0] = 1;
	for (int i = 1; i <= nn; i += 1)
		for (int j = 0; j <= std:: min(i, n); j += 1) {
			if (i - 2 * j >= k) continue;
			f[i][j] = (f[i - 1][j] + (j ? f[i - 1][j - 1] : 0)) % mod;
		}
	printf("%d\n", f[nn][n]);
	return 0;
}
posted @ 2018-11-03 15:58  Grary  阅读(153)  评论(0编辑  收藏  举报
博客园 首页 私信博主 编辑 关注 管理 新世界