I Hate Non-integer Number,(线性dp,计数类)

D - I Hate Non-integer Number (atcoder.jp)

Problem Statement

You are given a sequence of positive integers A=(a1​,…,aN​) of length N.
There are (2N−1) ways to choose one or more terms of A. How many of them have an integer-valued average? Find the count modulo 998244353.

Constraints

  • 1≤N≤100
  • 1≤ai​≤109
  • All values in input are integers.

Input

Input is given from Standard Input in the following format:

N
a1​ …… aN​

Output

Print the answer.

Sample 1

Inputcopy Outputcopy
3
2 6 2
6

For each way to choose terms of A, the average is obtained as follows:

  • If just a1​ is chosen,the average is an integer.

  • If just a2​ is chosen, the average is an integer.

  • If just a3​ is chosen,the average is an integer.

  • If a1​ and a2​ are chosen, the average is an integer.

  • If a1​ and a3​ are chosen,the average is an integer.

  • If a2​ and a3​ are chosen,  the average is an integer.

  • If a1​, a2​, and a3​ are chosen, the average is  not an integer.

Therefore, 66 ways satisfy the condition.

Sample 2

Inputcopy Outputcopy
5
5 5 5 5 5
31

Regardless of the choice of one or more terms of A, the average equals 5.

Sponsor

解析:

dp题我们要进行状态的划分,即集合的划分,且遵循一下划分原则:划分的状态不重不漏,且体现出转移所依据的状态

这到题是一道较难的题目,因此,我们需要发掘题目中的性质,利用性质解决该问题

这里我们可以发现,性质:题目中的有效状态是所选数字的和对所选数字个数取模等于 0

有发现数字个数最多不超过 100 ,因此我们可以使用 取模来进行其中的状态表示。

——————————————————————————————————————

集合划分:f[i][j][k]  表示:前 i 个数中选择 j 个,前 i 个数的和 % j 等于 k

状态转移:
1、f[i-1][j][k]

2、f[i-1][j-1][((k-a[i])%j+j)%j]

但我们发现第 2 个状态转移的中,((k-a[i])%j+j)%j 是想找:前 i-1 个数取 j-1 个,前 i-1 个数的和 % j 等于 ((k-a[i])%j+j)%j 。但实际上 f[i-1][j-1][((k-a[i])%j+j)%j] 前 i-1 个数取 j-1 个, 前 i-1 个数的和 % j-1 等于 ((k-a[i])%j+j)%j

因此这是状态转移过程是错误的。

——————————————————————————————————————————

我们可以改进一下:

集合划分:f[i][j][k]  表示:前 i 个数中选择 j 个,前 i 个数的和 % j 等于 k

状态转移:
1、f[i-1][j][k]

2、f[i-1][j-1][((k-a[i])%p+p)%p]

我们在最外层多加一层循环,表示 p ,这样就可以解决上述问题了。

答案就是 ans=(ans+f[n][p][0])%mod

时间复杂度位O(n^4),不会超时

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
typedef long long LL;
const int N = 1e2 + 5;
const LL mod = 998244353;
int n;
LL a[N], f[N][N][N];

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
	}
	LL ans = 0;
	for (int p = 1; p <= n; p++) {
		memset(f, 0, sizeof f);
		f[0][0][0] = 1;
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j <= i && j <= p; j++) {
				for (int k = 0; k <= p; k++) {
					f[i][j][k] = f[i - 1][j][k];
					if (j != 0) {
						f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][((k - a[i]) % p + p) % p]) % mod;
					}
					//printf("{%d %d %d}: %lld\n", i, j, k, f[i][j][k]);
				}
				
			}
		}
		ans = (ans + f[n][p][0]) % mod;
	}
	cout << ans << endl;
	return 0;
}

你可能感兴趣的:(#,计数类dp,算法,动态规划)