洛谷P4104 [HEOI2014]平衡(dp 组合数学)

题意

题目链接

Sol

可以把题目转化为从([1, 2n + 1])中选(k)个数,使其和为((n+1)k)

再转化一下:把((n+1)k)划分为(k)个数,满足每个数在范围在([1, 2n + 1])

这时候就可以用整数划分的思路dp了(然鹅我还是想不出来。。)

因为每个数互不相同,因此我们可以把每个阶段划分出来的数都看做不降的

(f[i][j])表示前(i)个数,和为(j)且满足条件的方案数。

我们考虑最小的数是否是(1)

若不是(1),则映射到所有数(-1),也就是(f[i][j - i])

若是(1),这时候相当于对于(f[i - 1][j - (i-1)])的所有数(+1),同时在最前面补上(1),方案为(f[i - 1][j - i])

然后再减去最大的数超过(2n+1)的方案,也就是(f[i][j - (2n + 2)])

复杂度(O(Tnk^2))

#include<bits/stdc++.h>
#define Fin(x) freopen(#x".in", "r", stdin);
using namespace std;
const int MAXN = 50001;
int mod;
template<typename A, typename B> inline bool chmax(A &x, B y) {return x < y ? x = y, 1 : 0;}
template<typename A, typename B> inline bool chmin(A &x, B y) {return x > y ? x = y, 1 : 0;}
template<typename A, typename B> inline A mul(A x, B y) {return 1ll * x * y % mod;}
template<typename A, typename B> inline void add2(A &x, B y) {x = x + y >= mod ? x + y - mod : x + y;}
template<typename A, typename B> inline int add(A &x, B y) {return x + y >= mod ? x + y - mod : x + y;}
inline int read() {
	char c = getchar(); int x = 0, f = 1;
	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}
int fp(int a, int p) {
    int base = 1;
    while(p) {
        if(p & 1) base = mul(base, a);
        a = mul(a, a);  p >>= 1;
    }
    return base;
}
int inv(int x) {
	return fp(x, mod - 2);
}
int f[101][100001];
void solve() {
	int N = read(), K = read(); mod = read();
	memset(f, 0, sizeof(f));
	f[0][0] = 1;
	for(int j = 1; j <= (N + 1) * K; j++)
		for(int i = 1; i <= min(j, K); i++) {
			f[i][j] = add(f[i][j - i], f[i - 1][j - i]);
			if(j >= 2 * N + 2) add2(f[i][j], -f[i - 1][j - (2 * N + 2)] + mod);
		}
	cout << f[K][(N + 1) * K] << '
';
}
signed main() {
	for(int T = read(); T--; solve());
	return 0;
}
原文地址:https://www.cnblogs.com/zwfymqz/p/10440040.html