JZOJ 6833. 2020.10.24【NOIP提高A组】T3.justice(DP)
JZOJ 6833. 2020.10.24【NOIP提高A组】T3.justice
题目大意
- 有 n n n个 x x x和 m m m个 y y y,每次其中 k k k个合并成一个,权值变为它们的平均值,求只剩一个数的权值有多少种可能。
- n , m , k ≤ 3000 n,m,k≤3000 n,m,k≤3000
- x , y ≤ 1 0 18 x,y≤10^{18} x,y≤1018
题解
- 如果 x = y x=y x=y,那么答案是 1 1 1,否则显然 x x x和 y y y无论是多少,答案都只和 n , m , k n,m,k n,m,k有关。
- 直接令 x = 0 , y = 1 x=0,y=1 x=0,y=1,考虑把初始的 n + m n+m n+m个数放在一棵 k k k叉树上, 0 0 0对最后权值的贡献是 0 0 0, 1 1 1对最后权值的贡献就是 ( 1 k ) d e e p (\frac{1}{k})^{deep} (k1)deep,其中 d e e p deep deep为这个数的深度。
- 那么最后的数就是 V = ∑ ( 1 k ) d e e p V=\sum(\frac{1}{k})^{deep} V=∑(k1)deep,可以理解为是一个 k k k进制下的小数,
- 如果没有进位,那这个数小数部分数位之和 S = m S=m S=m,每进位一次 S S S就要减去 k − 1 k-1 k−1,所以 S ≡ m ( m o d k − 1 ) S\equiv m\pmod {k-1} S≡m(modk−1),且 S ≤ m S≤m S≤m.
- 在某种操作方式下,如果把 0 0 0和 1 1 1交换,那么 1 − V 1-V 1−V表示成的 k k k进制数数位之和 S ′ S' S′就应该满足 S ′ ≡ n ( m o d k − 1 ) S'\equiv n\pmod {k-1} S′≡n(modk−1),且 S ≤ n S≤n S≤n.
- 而此时的 S ′ S' S′是可以通过 S S S计算出来的,若这个小数位数为 l l l,则 S ′ = ( l − 1 ) ∗ ( k − 1 ) + k − S S'=(l-1)*(k-1)+k-S S′=(l−1)∗(k−1)+k−S,类似于 1.000 … 1.000… 1.000…减去某个数列竖式减法。
- 这样就相当于要构造一个 k k k进制小数,满足数位和为 S S S, S S S满足上面的限制, S S S对应的 S ′ S' S′也要满足上面的限制,求方案数。
- 设 f i , j f_{i,j} fi,j表示前 i i i位和为 j j j的方案数, O ( n ) O(n) O(n)转移,总复杂度 O ( n 3 ) O(n^3) O(n3),用前缀和可以优化到 O ( n 2 ) O(n^2) O(n2)。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define md 1000000007
#define ll long long
#define N 3010
ll f[N * 2][N][2], g[N];
int main() {
int n, m, k, i, j; ll x, y;
scanf("%d%d%d%lld%lld", &m, &n, &k, &x, &y);
f[0][0][0] = 1;
for(i = 1; i <= (n + m) / (k - 1); i++) {
g[0] = (f[i - 1][0][0] + f[i - 1][0][1]) % md;
for(j = 1; j <= n; j++) g[j] = (g[j - 1] + f[i - 1][j][0] + f[i - 1][j][1]) % md;
for(j = 0; j <= n; j++) {
if(j) {
if(j - k < 0) f[i][j][1] = g[j - 1];
else f[i][j][1] = (g[j - 1] - g[j - k] + md) % md;
}
f[i][j][0] = (f[i - 1][j][0] + f[i - 1][j][1]) % md;
}
}
ll ans = 0;
for(i = 0; i <= (n + m) / (k - 1); i++) {
for(j = 0; j <= n; j++)
if(j % (k - 1) == n % (k - 1) && i * (k - 1) - j + 1 <= m) (ans += f[i][j][1]) %= md;
}
if(x == y) ans = 1;
printf("%lld\n", ans);
return 0;
}
哈哈哈哈哈哈哈哈哈哈