AtCoder AGC009E Eternal Average (DP)

题目链接

https://atcoder.jp/contests/agc009/tasks/agc009_e

题解

又被劝退了。。。
第一步转化非常显然: 就等价于一开始有一个数(1), 有(frac{n+m-1}{k-1})次机会每次选择一个数把它变成(k)个原来的(frac{1}{k}), 最后从(n+m)个数中选出(m)个,问能选出多少不同的数。
然后考虑化成(k)进制小数,假设最后形成的数是(d_1,d_2,...,d_{n+m}), 则(sum^{n+m}_{i=1} d_i=1).
一个(d)进制小数可以被表示成(m)(k)的负整数次幂之和当且仅当其每一位数值之和不超过(m)且和(m)((k-1))同余。(显然)
但同时还要保证(1)可以被表示成((n+m))(k)的负整数次幂之和,且包含这(m)个数。那么就可以转化成(1)减这个小数可以被表示成(n)(k)的负整数次幂之和。(行吧我就这一步没想出来……自闭了啊……)
所以最后也就是要计算有多少个序列(a_1,a_2,...,a_l (1le lle frac{n+m-1}{k-1})), 满足(0le a_ile k-1, a_l>0, sum^l_{i=1}a_ile m,sum^l_{i=1}a_iequiv m(mod k-1), sum^l_{i=1}k-1-a_ile n-1, sum^l_{i=1}k-1-a_iequiv n-1(mod k-1)), 直接dp即可。时间复杂度(O((n+m)k)).

代码

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cassert>
#define llong long long
using namespace std;

const int N = 4e3;
const int P = 1e9+7;
llong dp[N+3][N+3],sdp[N+3][N+3];
int n1,n2,m,len;
llong ans;

int main()
{
	scanf("%d%d%d",&n1,&n2,&m); len = (n1+n2-1)/(m-1);
	dp[0][0] = 1ll; for(int j=0; j<=n2; j++) sdp[0][j] = 1;
	for(int i=1; i<=len; i++)
	{
		for(int j=0; j<=n2; j++)
		{
			if(j>=m) {dp[i][j] = (sdp[i-1][j]-sdp[i-1][j-m]+P)%P;}
			else {dp[i][j] = sdp[i-1][j];}
			if((n2-j)%(m-1)==0 && i*(m-1)-j<=n1-1 && (n1-1-i*(m-1)+j)%(m-1)==0) {ans = (ans+dp[i][j]-dp[i-1][j]+P)%P;}
		}
		sdp[i][0] = dp[i][0]; for(int j=1; j<=n2; j++) sdp[i][j] = (sdp[i][j-1]+dp[i][j])%P;
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/suncongbo/p/11519281.html