JZOJ 5192. 【NOI2017模拟7.2】容器 (dp)

https://gmoj.net/senior/#contest/show/2376/1

题解:

考虑把区间拆成左端点和右端点,\(T\)的限制相当于任何一个时候,没有匹配的左端点\(\le T\)

\(f[i][p][q]\)表示刚刚确定了\(i\)的右括号,和\(i+1\)的左括号,匹配了\(p\)个区间,还有\(q\)左括号。

转移可以枚举下一步选多少,顺便分配是那些跳蚤选了这些左括号和右括号就行了。

一直纠结于用\(\frac{k!}{\prod a[i]!}\)去计算分配方案,事实上不用,这样也做不了。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int mo = 1011110011;

const int N = 45;

int n, k, t;
ll c[N][N];

ll f[N][N][N];

void add(ll &x, ll y) {
	(x += y) %= mo;
}

int main() {
	freopen("container.in", "r", stdin);
	freopen("container.out", "w", stdout);
	scanf("%d %d %d", &n, &k, &t);
	fo(i, 0, 40) {
		c[i][0] = 1;
		fo(j, 1, i) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mo;
	}
	f[0][0][0] = 1;
	n ++;
	fo(i, 1, n) {
		fo(p, 0, k) fo(q, 0, k) if(f[i - 1][p][q]) {
			fo(u, 0, q)	fo(v, 0, k - p - q) {
				int np = p + u, nq = q - u + v;
				if(np + nq > k || nq > t) continue;
				add(f[i][np][nq], f[i - 1][p][q] * c[q][u] % mo * c[k - p - q][v]);
			}
		}
	}
	ll ans = f[n][k][0];
	pp("%lld\n", ans);
}

posted @ 2020-05-18 22:28  Cold_Chair  阅读(133)  评论(0编辑  收藏  举报