??Link.
??读题时间≈想题时间,草。(
??给定 \(N,K,M\),对于每个 \(x\in[1,N]\) 的整数 \(x\),统计多重集 \(\{s\}\) 的个数,使得集合元素的平均数为 \(x\),且满足对于任意 \(i\), \(s_i\in[1,N]\) 且 \(\sum_j[s_i=s_j]\le K\),即相同元素至多出现 \(K\) 次。答案对 \(M\) 取模。
??\(N,K\le100\)。
??首先,看到“平均数”“中位数”之类的 AtCoder 比较喜欢的东西,肯定要进行转化。这里可以转化为“偏移量”:假设已钦定平均数 \(x\),那么一个元素 \(v\) 的贡献就是 \(v-x\),最终满足贡献和为 \(0\) 即可。对于任意多重集 \(\{s\}\),记 \(i\) 在其中出现的次数为 \(k_i~(i\in[1,N])\),那么:
??写着形象点就是满足:
??然后非常 amazing 地发现等号左右描述的是同一形式的问题:求 \(\{k_n\}\) 的个数,使得 \(\sum_{i=1}^nik_i=s\),且满足 \(k_i\in[0,K]\),其中 \(n,s\) 是形式参数。这个问题显然可以 DP:令 \(f(i,j)\) 表示 \(\sum_{t=1}^itk_t=j\) 的方案数,枚举新加入的 \(k_{i+1}\) 的值即可转移。
??第一维大小显然为 \(n\),考虑第二维的大小,我们只需要求出能使等式左右两边同时取到的 \(j\) 值。所以 \(j\le K(1+2+\cdots+\lfloor\frac{n}2\rfloor)\)。
??最后,求答案时,枚举等式左右同时取到的值,乘法原理乘起来,别忘了乘上 \(k_x\) 的 \(K+1\) 种取值,在减去空集的 \(1\)。(真啰嗦 owo。
??总复杂度 \(\mathcal O(N^3K^2)\) 带小于 \(\frac{1}8\) 的巨小常数(有问题欢迎指出 w),DP 建议刷表,考虑到无用状态数较多,跑得可快啦!
/* Clearink */
#include <cstdio>
inline void wint ( int x ) {
if ( 9 < x ) wint ( x / 10 );
putchar ( x % 10 ^ ‘0‘ );
}
const int MAXN = 100;
int N, K, M, f[MAXN + 1][MAXN * ( MAXN / 2 + 1 ) * ( MAXN >> 1 ) / 2 + 5];
inline void addeq ( int& a, const int b ) { ( a += b ) < M ? 0 : a -= M; }
inline void initDP () {
f[0][0] = 1;
int sbound = K * ( N / 2 + 1 ) * ( N >> 1 ) >> 1;
for ( int i = 0; i < N; ++ i ) {
for ( int j = 0, cur; j <= sbound; ++ j ) {
if ( !( cur = f[i][j] ) ) continue;
for ( int k = 0, s = j; k <= K && s <= sbound; ++ k, s += i + 1 ) {
addeq ( f[i + 1][s], cur );
}
}
}
}
inline int solve ( const int x ) {
// k[x-1]+2k[x-2]+...+(x-1)k[1] = k[x+1]+2k[x+2]+...+(n-x)k[n].
int sbound = x - 1 < N - x ? x - 1 : N - x, ret = 0;
sbound = K * sbound * ( sbound + 1 ) >> 1;
for ( int s = 0; s <= sbound; ++ s ) {
addeq ( ret, 1ll * f[x - 1][s] * f[N - x][s] % M );
}
return ( ret * ( K + 1ll ) % M + M - 1 ) % M;
}
int main () {
scanf ( "%d %d %d", &N, &K, &M );
initDP ();
for ( int i = 1; i <= N; ++ i ) {
wint ( solve ( i ) );
putchar ( ‘\n‘ );
}
return 0;
}
Solution -「ARC 104D」Multiset Mean
原文:https://www.cnblogs.com/rainybunny/p/13817640.html