找出所有的 \(k\) 使 \(a\) 的所有长度为 \(k\) 的子串都可以被分为和相等的两部分。
赛场上的想法是通过某种神奇的方式快速删一个加一个或者使处理出来一块的结果被很多块用上之类的,然后想着 \(f_i\) 表示组成 \(i\) 的第一个的最后面的位置。笑死,有了这个 \(f\) 直接就正解了还需要那个?
对于每个右端点的 \(f\) 若组成一段的和的 \(f\) 值比那个端点还要前面那就肯定不行了,反之一定可以,这样就实现了 \(O(1)\) 的判断,枚举左端点即可。
一个关键的“知识点”是跑 \(O(10^9)\) 常数较小的运算如加减和比较赋值是完全可以的,特别是在有 O2 的时候。
#include <iostream>
#include <bitset>
#include <algorithm>
const int N = 1005;
int T, a[N], n, ans[N], f[100005], sum[N];
int main() {
freopen("array.in", "r", stdin);
freopen("array.out", "w", stdout);
std::ios::sync_with_stdio(false), std::cin.tie(nullptr);
std::cin >> T;
while (T--) {
std::cin >> n;
for (int i = 1; i <= n; i++) sum[i] = 0, ans[i] = 1;
for (int i = 1; i <= n; i++) std::cin >> a[i], sum[i] = sum[i-1] + a[i];
for (int j = 0; j <= sum[n]; j++) f[j] = -1;
for (int i = 1; i <= n; i++) {
f[0] = i;
for (int j = sum[n]; j >= a[i]; j--)
f[j] = std::max(f[j], f[j-a[i]]);
for (int j = 1; j <= i; j++)
if ((sum[i]-sum[j-1]&1) || f[(sum[i]-sum[j-1])/2] < j)
ans[i-j+1] = 0;
}
int cnt = 0;
for (int i = 1; i <= n; i++) cnt += ans[i];
std::cout << cnt << ' ';
for (int i = 1; i <= n; i++) if (ans[i]) std::cout << i << ' ';
std::cout << '\n';
}
}