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<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
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;
}