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,k3000
  • x , y ≤ 1 0 18 x,y≤10^{18} x,y1018

题解

  • 如果 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 k1,所以 S ≡ m ( m o d k − 1 ) Sequiv mpmod {k-1} Sm(modk1),且 S ≤ m S≤m Sm.
  • 在某种操作方式下,如果把 0 0 0 1 1 1交换,那么 1 − V 1-V 1V表示成的 k k k进制数数位之和 S ′ S' S就应该满足 S ′ ≡ n ( m o d k − 1 ) S'equiv npmod {k-1} Sn(modk1),且 S ≤ n S≤n Sn.
  • 而此时的 S ′ S' S是可以通过 S S S计算出来的,若这个小数位数为 l l l,则 S ′ = ( l − 1 ) ∗ ( k − 1 ) + k − S S'=(l-1)*(k-1)+k-S S=(l1)(k1)+kS,类似于 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
", ans);
	return 0;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910015.html