洛谷P2523 [HAOI2011]Problem c(计数dp)

题面

luogu

题解

首先,显然一个人实际位置只可能大于或等于编号

先考虑无解的情况

对于编号为\(i\),如果确认的人编号在\([i,n]\)中数量大于区间长度,那么就无解

\(S[i]\)表示确认的人编号在\([i,n]\)中数量

我们只要考虑剩下的\(n - m\)

\(f[i][j]\)表示编号\(>=i\)的,已经确认了\(j\)

那么我们枚举多少人编号为\(i\)

\(f[i][j] = \sum f[i + 1][j - k] * (^j_k)\)

因为交换一些人的编号也是可行方案,所以乘上一个组合数

Code

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
	x = 0; RG char c = getchar(); bool f = 0;
	while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
	while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
	x = f ? -x : x;
	return ;
}
template<class T> inline void write(T x) {
	if (!x) {putchar(48);return ;}
	if (x < 0) x = -x, putchar('-');
	int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
	for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

const int N = 310;

int n, m, Mod, s[N], f[N][N], C[N][N];
void pls(int &x, int y) {
	x += y;
	if (x >= Mod) x -= Mod;
}
void solve() {
	read(n), read(m), read(Mod);
	for (int i = 0; i <= 300; i++) C[i][0] = C[i][i] = 1;
	for (int i = 2; i <= 300; i++)
		for (int j = 1; j < i; j++)
			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % Mod;
	memset(s, 0, sizeof(s));
	for (int i = 1, x; i <= m; i++) read(x), read(x), s[x]++;
	for (int i = n; i; i--) s[i] += s[i + 1];
	bool flag = 0;
	for (int i = 1; i <= n; i++)
		if (s[i] > n - i + 1) {
			flag = 1;
			break;
		}
	if (flag) {
		puts("NO");
		return ;
	}
	memset(f, 0, sizeof(f));
	f[n + 1][0] = 1;
	for (int i = n; i; i--)
		for (int j = 0; j <= n - i + 1 - s[i]; j++)
			for (int k = 0; k <= j; k++)
				pls(f[i][j], 1ll * f[i + 1][j - k] * C[j][k] % Mod);
	printf("YES %d\n", f[1][n - m]);
	return ;
}

int main() {
	int T;
	read(T);
	while (T--) solve();
	return 0;
}

posted @ 2019-03-17 22:23  zzy2005  阅读(137)  评论(0编辑  收藏  举报