[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 1≤n,m≤2000,2≤k≤2000
保证 n + m − 1 n+m-1 n+m−1 能被 k − 1 k-1 k−1 整除。
题解
我是 *。离正解就差一步了,我**的死脑筋就不推了。我是 *。
首先很容易看出来,答案一定是给每个1和0配上一个 k − x k^{-x} k−x 的系数再加起来,满足 ∑ k − x i = 1 \sum k^{-x_i}=1 ∑k−xi=1,求 Z = ∑ a i = 1 k − x i Z=\sum_{a_i=1}k^{-x_i} Z=∑ai=1k−xi 有多少种取值。
显然需要依次考虑 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=1k−xi 从低位到高位求和的过程中没有进位,那么肯定满足 ∑ z i = n \sum z_i=n ∑zi=n,若考虑进位,那么满足 ∑ z i ≤ n \sum z_i\le n ∑zi≤n。但是 ∑ z i \sum z_i ∑zi 或者说每一个 z i z_i zi 具体的限制究竟是什么?如果你像傻*我一样到了这里就不继续推导而想着优化暴力DP的话,那么你最终会在发现自己的四次方五次方DP怎么都会把方案算重的时候崩溃掉。(有人在拉这道题的时候希望设置 n , m , k ≤ 10 n,m,k\le 10 n,m,k≤10 的部分分,可真是太敢屑了)
其实我们可以很轻易地发现,当产生进位的时候,低位处减少 x ∗ k x*k x∗k ,高位处会增加 x x x,此时 ∑ z i \sum z_i ∑zi 刚好减少 k − 1 k-1 k−1 的整数倍,也就是说 ∑ z i \sum z_i ∑zi 仍满足 ∑ z i = n ( m o d k − 1 ) \sum z_i=n(\bmod k-1) ∑zi=n(modk−1)。归纳验证发现, ∑ z i ≤ n \sum z_i\le n ∑zi≤n 和 ∑ z i = n ( m o d k − 1 ) \sum z_i=n(\bmod k-1) ∑zi=n(modk−1) 这两条限制已经足够。
还剩下 ∑ k − x i = 1 \sum k^{-x_i}=1 ∑k−xi=1 这个条件,我们可以发现它其实就等同于 ∑ a i = 0 k − x i = 1 − Z \sum_{a_i=0}k^{-x_i}=1-Z ∑ai=0k−xi=1−Z,我们把 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) 1−Z=0.(k−1−z1)(k−1−z2)...(k−1−zp−1)(k−zp) 套用上面的推导即可得出 1 + ( k − 1 ) p − ∑ z i ≤ m 1+(k-1)p-\sum z_i\le m 1+(k−1)p−∑zi≤m 和 1 + ( k − 1 ) p − ∑ z i = m ( m o d k − 1 ) 1+(k-1)p-\sum z_i=m(\bmod k-1) 1+(k−1)p−∑zi=m(modk−1) 的限制。
然后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[i−1][j][0]+dp[i−1][j][1]dp[i][j][1]=x=1∑k−1(dp[i−1][j−x][0]+dp[i−1][j−x][1])
然后就做完了。不需要前缀和优化,因为第一维不超过总操作数,故直接转移复杂度是
O
(
n
+
m
−
1
k
−
1
k
n
)
=
O
(
n
2
)
O(\frac{n+m-1}{k-1}kn)=O(n^2)
O(k−1n+m−1kn)=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;
}