题目内容
给定一个长度为 n n n 的整数数组 a a a ,问有多少个不同的下标对 i i i 和 j j j 满足 a i + a j a_i+a_j ai+aj 的加法中没有进位。
数据范围
2
≤
n
≤
1
0
6
2\leq n\leq 10^6
2≤n≤106
0
≤
a
i
<
1
0
6
0\leq a_i <10^6
0≤ai<106
题解
十进制的子集DP
f [ j ] [ i ] f[j][i] f[j][i] 表示前 j j j 位(第1位是个位,第2位是十位)的数 i i i 的子集
- 考虑前
j
−
1
j-1
j−1 位
f [ j ] [ i ] = f [ j − 1 ] [ i ] f[j][i]=f[j-1][i] f[j][i]=f[j−1][i] - 考虑第
j
j
j 位的数
i
i
i 的子集
i − 1 0 j − 1 , i − 2 × 1 0 j − 1 , . . . i-10^{j-1}, i-2\times 10^{j-1},... i−10j−1,i−2×10j−1,...
需要考虑的是 i − 1 0 j − 1 i- 10^{j-1} i−10j−1 的子集也是 i i i 的子集,所以只需要加上 i − 1 0 j − 1 i-10^{j-1} i−10j−1 的子集即可。
f [ j ] [ i ] = f [ j ] [ i ] + f [ j ] [ i − 1 0 j − 1 ] f[j][i]=f[j][i]+f[j][i-10^{j-1}] f[j][i]=f[j][i]+f[j][i−10j−1]
因为每次只用到前一位的状态,所以可以直接滚动数组优化。
时间复杂度: O ( 6 n ) O(6n) O(6n) , 6 6 6 是因为值域至多是 6 6 6 位数
代码
#include <bits/stdc++.h>
using namespace std;
const int MAX = 1000000;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
vector<long long> f(MAX);
for (int i = 0; i < n; ++i) {
cin >> a[i];
f[a[i]] += 1;
}
// f[j][i] 表示只考虑十进制的前 j 位 (个位是第一位),i 的子集的数量
// f[j][i] = f[j - 1][i]
// 那么只考虑第 j 位
// 对于第 j 位,i-10^{j-1}, i-2*10^{j-1} ... 都是其子集
// 但是 f[i-10^{j-1}] 的子集也是 f[i][j] 的子集,所以只考虑这个即可
// f[j][i] += f[j][i-10^{j-1}]
for (int j = 1; j < MAX; j *= 10) {
for (int i = 0; i < MAX; ++i) {
if (i / j % 10 > 0) {
f[i] += f[i - j];
}
}
}
long long ans = 0;
for (int x: a) {
ans += f[999999 - x];
bool ok = true;
while (x > 0) {
if (x % 10 > 4) {
ok = false;
break;
}
x /= 10;
}
if (ok) ans -= 1;
}
cout << ans / 2 << "\n";
return 0;
}