CodeFestival16FinalH

题意

\(A\)\(B\) 玩游戏。有 \(n\) 个数,从左到右 \(1\)\(n\),每个数有权值 \(a_i\)\(A\) 在第 \(1\) 个数上,\(B\) 在第 \(2\) 个数上,每次左边的那个人选择右边一个没有被人占过的位置过去,若无这样的位置游戏结束。两人的得分为所占的所有格子的权值和。

\(q\) 次询问,每次询问给 \(x\),问 \(a_n\ =\ x\)\(A\) 得分 \(-\ B\) 得分最大值是多少。(两人均最优策略)

\(1\ \leq\ n\ \leq\ 200000,\ 0\ \leq\ a_i\ \leq\ 10^6,\ \sum_{i\ =\ 1}^{n\ -\ 1}\ a_i\ \leq\ 10^6,\ q\ \leq\ 200000,\ 0\ \leq\ x\ \leq\ 10^9\)

做法1

\(dp_i\) 表示 \([i...n]\) 游戏先手 \(-\) 后手得分的最大值,可以发现 \(dp_i\ =\ |a_i\ -\ dp_{i+1}|\)
\(f_{i,x}\) 表示当 \(dp_{i+1}\ =\ x\) 时,\(dp_1\) 的值。则 \(f_{i,x}\ =\ f_{i-1,|a_i-x|}\),即将 \(f_{i-1}\) 向右平移 \(a_i\) 位,再以 \(a_i\) 为轴翻转。用 \(deque\) 维护即可。
每次询问找到后缀和 \(>\ x\) 的最大的 \(i\),答案就是 \(f_{i,x-\sum_{j>i}a_j}\)

时间复杂度 \(O(n\ +\ q\ +\ \sum_{i\ =\ 1}^{n\ -\ 1}\ a_i)\)

代码

#include <bits/stdc++.h>

#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif

using namespace std;

int main() {
	ios::sync_with_stdio(false);
	int n, s = 0, base = 0;
	cin >> n;
	--n;
	int foo, bar;
	cin >> foo >> bar;
	base = foo - bar;
	n -= 2;
	vector<int> a(n), suf_sum(n + 1, 0);
	vector<vector<pair<int, int> > > all(n);
	for (int i = 0; i < n; ++i) cin >> a[i], s += a[i];
	vector<int> rem(s);
	for (int t = 0, i = n - 1; ~i; --i) {
		suf_sum[i] = suf_sum[i + 1] + a[i];
		for (int j = 0; j < a[i]; ++j) rem[t + j] = i;
		t += a[i];
	}
	int m;
	cin >> m;
	vector<int> q(m), ans(m);
	for (int i = 0; i < m; ++i) {
		cin >> q[i];
		if(q[i] >= s) ans[i] = q[i] - s + base;
		else all[rem[q[i]]].push_back(make_pair(i, q[i] - suf_sum[rem[q[i]] + 1]));
	}
	deque<int> dp;
	for (int i = 0; i < n; ++i) {
		while(dp.size() <= a[i]) dp.push_back(dp.size() == 0 ? 0 : dp.back() + 1);
		for (int j = dp.size() - 1, end_j = j - a[i]; j > end_j; --j) dp.push_front(dp[dp.size() - j]);
		for (auto t: all[i]) ans[t.first] = dp[t.second] + base;
	}
	copy(ans.begin(), ans.end(), ostream_iterator<int>(cout, "\n"));
	return 0;
}
posted @ 2018-11-17 21:13  King_George  阅读(300)  评论(0编辑  收藏  举报