0
点赞
收藏
分享

微信扫一扫

[AGC009E] Eternal Average——结论、DP

求阙者 2022-02-13 阅读 18

[AGC009E] Eternal Average

题目描述

黑板上有 n n n 个0和 m m m 个1,我们每次选择 k k k 个数字将其擦除,然后把它们的平均数写上去,这样一直操作直到只剩下一个数字,问剩下的这个数字有多少种不同的情况。

答案对 1 0 9 + 7 10^9+7 109+7 取模

1 ≤ n , m ≤ 2000 , 2 ≤ k ≤ 2000 1 \leq n,m \leq 2000,2 \leq k \leq 2000 1n,m2000,2k2000

保证 n + m − 1 n+m-1 n+m1 能被 k − 1 k-1 k1 整除。

题解

我是 *。离正解就差一步了,我**的死脑筋就不推了。我是 *。

首先很容易看出来,答案一定是给每个1和0配上一个 k − x k^{-x} kx 的系数再加起来,满足 ∑ k − x i = 1 \sum k^{-x_i}=1 kxi=1,求 Z = ∑ a i = 1 k − x i Z=\sum_{a_i=1}k^{-x_i} Z=ai=1kxi 有多少种取值。

显然需要依次考虑 k k k 进制下 Z Z Z 的每个小数位的取值。设 Z = 0. z 1 z 2 . . . z p , z p > 0 Z=0.z_1z_2...z_p,z_p>0 Z=0.z1z2...zp,zp>0,假设 ∑ a i = 1 k − x i \sum_{a_i=1}k^{-x_i} ai=1kxi 从低位到高位求和的过程中没有进位,那么肯定满足 ∑ z i = n \sum z_i=n zi=n,若考虑进位,那么满足 ∑ z i ≤ n \sum z_i\le n zin。但是 ∑ z i \sum z_i zi 或者说每一个 z i z_i zi 具体的限制究竟是什么?如果你像傻*我一样到了这里就不继续推导而想着优化暴力DP的话,那么你最终会在发现自己的四次方五次方DP怎么都会把方案算重的时候崩溃掉。(有人在拉这道题的时候希望设置 n , m , k ≤ 10 n,m,k\le 10 n,m,k10 的部分分,可真是太敢屑了)

其实我们可以很轻易地发现,当产生进位的时候,低位处减少 x ∗ k x*k xk ,高位处会增加 x x x,此时 ∑ z i \sum z_i zi 刚好减少 k − 1 k-1 k1 的整数倍,也就是说 ∑ z i \sum z_i zi 仍满足 ∑ z i = n (   m o d   k − 1 ) \sum z_i=n(\bmod k-1) zi=n(modk1)。归纳验证发现, ∑ z i ≤ n \sum z_i\le n zin ∑ z i = n (   m o d   k − 1 ) \sum z_i=n(\bmod k-1) zi=n(modk1) 这两条限制已经足够。

还剩下 ∑ k − x i = 1 \sum k^{-x_i}=1 kxi=1 这个条件,我们可以发现它其实就等同于 ∑ a i = 0 k − x i = 1 − Z \sum_{a_i=0}k^{-x_i}=1-Z ai=0kxi=1Z,我们把 1 − Z = 0. ( k − 1 − z 1 ) ( k − 1 − z 2 ) . . . ( k − 1 − z p − 1 ) ( k − z p ) 1-Z=0.(k-1-z_1)(k-1-z_2)...(k-1-z_{p-1})(k-z_p) 1Z=0.(k1z1)(k1z2)...(k1zp1)(kzp) 套用上面的推导即可得出 1 + ( k − 1 ) p − ∑ z i ≤ m 1+(k-1)p-\sum z_i\le m 1+(k1)pzim 1 + ( k − 1 ) p − ∑ z i = m (   m o d   k − 1 ) 1+(k-1)p-\sum z_i=m(\bmod k-1) 1+(k1)pzi=m(modk1) 的限制。

然后DP的转移就很明显了,只需要设 d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1] 表示考虑到小数点后第 i i i 位, ∑ x = 1 i z x = j \sum_{x=1}^i z_x=j x=1izx=j z i z_i zi 等于/大于0时的取值个数,转移方程为
d p [ i ] [ j ] [ 0 ] = d p [ i − 1 ] [ j ] [ 0 ] + d p [ i − 1 ] [ j ] [ 1 ] d p [ i ] [ j ] [ 1 ] = ∑ x = 1 k − 1 (   d p [ i − 1 ] [ j − x ] [ 0 ] + d p [ i − 1 ] [ j − x ] [ 1 ]   ) dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1]\\ dp[i][j][1]=\sum_{x=1}^{k-1}(\,dp[i-1][j-x][0]+dp[i-1][j-x][1]\,) dp[i][j][0]=dp[i1][j][0]+dp[i1][j][1]dp[i][j][1]=x=1k1(dp[i1][jx][0]+dp[i1][jx][1])
然后就做完了。不需要前缀和优化,因为第一维不超过总操作数,故直接转移复杂度是 O ( n + m − 1 k − 1 k n ) = O ( n 2 ) O(\frac{n+m-1}{k-1}kn)=O(n^2) O(k1n+m1kn)=O(n2)

代码

#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
using namespace std;
const int MAXN=4005;
const ll INF=1e18;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	ptf[lpt=1]=x%10;
	while(x>9)x/=10,ptf[++lpt]=x%10;
	while(lpt)putchar(ptf[lpt--]^48);
	if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}

const ll MOD=1e9+7;
int n,m,k,p;
ll dp[MAXN][MAXN][2],ans;
inline void ad(ll&a,ll b){a+=b;if(a>=MOD)a-=MOD;}
signed main()
{
	n=read(),m=read(),k=read(),p=(n+m-1)/(k-1);
	dp[0][0][0]=1;
	for(int i=1;i<=p;i++){
		for(int j=0;j<=n;j++){
			dp[i][j][0]=(dp[i-1][j][0]+dp[i-1][j][1])%MOD;
			for(int x=1;x<k&&x<=j;x++)
				ad(dp[i][j][1],dp[i-1][j-x][1]),
				ad(dp[i][j][1],dp[i-1][j-x][0]);
		}
		for(int j=0;j<=n;j++)
			if(j%(k-1)==n%(k-1)&&((k-1)*i+1-j)<=m&&((k-1)*i+1-j)%(k-1)==m%(k-1))
				ad(ans,dp[i][j][1]);
	}
	print(ans);
	return 0;
}
举报

相关推荐

0 条评论