Atcoder ARC160C Power Up

发布时间 2023-07-09 00:36:19作者: lhzawa

首先有个很朴素的 DP:
\(f_{i, j}\) 为有 \(j\) 个数 \(i\) 能为数 \(i + 1\) 产生贡献的方案数和,这个状态的转移方程要好想一些:
\(f_{i, \frac{j + c_i}{2}} = \sum\limits_{k = j}^{2\times10^5} f_{i - 1, k}\),其中 \(c_i = \sum\limits_{j = 1}^n [a_j = i]\),然后这个式子很明显可以后缀和优化,于是转移就可以 \(O(1)\) 了。

但是发现 \(O(nV)\) 的状态数明显是会爆的,但是仔细想一下因为每个式子 \(j\) 那一位都会有一个 \(\frac{1}{2}\),这则说明 \(f_{i ,j}>0\) 的状态若 \(c_i = 0\) 的情况下肯定很快就只剩下 \(f_{i, 0} > 0\) 了。
其实 \(c_i > 0\) 也只是 \(i\) 这一轮 \(f_{i, j} > 0\) 的数量多了,后面还是会等倍缩小直至 \(0\)
于是可以动态开每一个 \(i\) 的空间转移,总状态数上限为 \(2n\),因为每个 \(a_i\) 对状态数的贡献是 \(a_i\times(1 + \frac{1}{2} + \frac{1}{4}+\cdots)\) 这样的。

// lhzawa(https://www.cnblogs.com/lhzawa/)
// Problem: C - Power Up
// Contest: AtCoder - AtCoder Regular Contest 160
// URL: https://atcoder.jp/contests/arc160/tasks/arc160_c
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 100;
vector<int> f[N];
int a[N];
const int mod = 998244353;
int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		int x;
		scanf("%d", &x);
		a[x]++;
	}
	f[0].push_back(1);
	for (int i = 1; i <= (int)(2e5) + 25; i++) {
		f[i].resize((a[i] + (int)(f[i - 1].size())) / 2 + 1);
		for (int j = 0; j < (int)(f[i - 1].size()); j++) {
			f[i][(j + a[i]) / 2] = (f[i][(j + a[i]) / 2] + f[i - 1][j]) % mod;
		}
		for (int j = (int)(f[i].size()) - 2; j >= 0; j--) {
			f[i][j] = (f[i][j] + f[i][j + 1]) % mod;
		}
	}
	printf("%d\n", f[(int)(2e5) + 25][0]);
	return 0;
}