【省内训练2019-06-02】互膜

本文探讨了在算法竞赛中如何运用博弈论思想解决一类特定的卡片游戏问题,通过数学推导得出最优策略,利用数据结构如树状数组和平衡树进行高效计算,实现了时间复杂度为O(MLogN+NLogN)的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【思路要点】

  • aia_iai 为卡牌的当前价值, Mini=Min{ai,ai+1,…,aN}Min_i=Min\{a_i,a_{i+1},\dots,a_N\}Mini=Min{ai,ai+1,,aN}
  • 引理: Ans=Min1+∑i≡1 (mod 2)Max{ai,Mini+1}Ans=Min_1+\sum_{i\equiv1\ (mod\ 2)}Max\{a_i,Min_{i+1}\}Ans=Min1+i1 (mod 2)Max{ai,Mini+1}
    证明:
    首先,先手玩家一定可以通过反转后手玩家的操作使得原本属于自己的权值到手。
    并且,若先手玩家选择不反转后手玩家当前的操作,则意味着先手玩家放弃当前后手玩家所操作的数 iii ,但由于先手玩家最终一定会具有 N2+1\frac{N}{2}+12N+1 个数,因此先手玩家在后续的过程中一定将会多出一个数,其最差情况为 Mini+1Min_{i+1}Mini+1 ,并且后手玩家可以通过操作非 Mini+1Min_{i+1}Mini+1 的位置使得最差情况取到。
  • 因此,考虑 aia_iai 从后向前的单调队列,由在队列中的点分成的各段 [l,r)[l,r)[l,r) 对答案的贡献即为 aia_iai(l,r)(l,r)(l,r) 内奇数位和以及端点 lll 处的贡献。
  • 注意到操作仅包含减法,用树状数组维护 aia_iai 的奇数位,并用平衡树维护单调队列即可,每次操作单调队列至多加入一个元素,因此单调队列的总变化是 O(N+M)O(N+M)O(N+M) 的。
  • 时间复杂度 O(MLogN+NLogN)O(MLogN+NLogN)O(MLogN+NLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct BinaryIndexTree {
	int n; ll a[MAXN];
	void init(int x) {
		n = x;
		memset(a, 0, sizeof(a));
	}
	void modify(int x, int d) {
		for (int i = x; i <= n; i += i & -i)
			a[i] += d;
	}
	ll query(int x) {
		ll ans = 0;
		for (int i = x; i >= 1; i -= i & -i)
			ans += a[i];
		return ans;
	}
	ll query(int l, int r) {
		if (l > r) return 0;
		ll ans = 0;
		for (int i = r; i >= 1; i -= i & -i)
			ans += a[i];
		for (int i = l - 1; i >= 1; i -= i & -i)
			ans -= a[i];
		return ans;
	}
} BIT;
int n, m, a[MAXN];
set <int> st; ll ans;
ll calc(int l, int r) {
	ll ans = BIT.query(l + 1, r - 1);
	if ((l & 1) && l < r) ans += a[r];
	return ans;
}
ll getans() {
	int pos = *st.begin();
	ll res = ans + BIT.query(1, pos - 1);
	return res + a[pos];
}
int main() {
	read(n), BIT.init(n);
	for (int i = 1; i <= n; i++) {
		read(a[i]);
		if (i & 1) BIT.modify(i, a[i]);
	}
	int Min = 2e9;
	for (int i = n; i >= 1; i--)
		if (a[i] < Min) {
			Min = a[i];
			if (i != n) ans += calc(i, *st.begin());
			st.insert(i);
		}
	writeln(getans()), read(m);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		auto pos = st.lower_bound(x); int last = *pos;
		if (*pos == x && x != n) {
			auto suf = pos; suf++;
			ans -= calc(*pos, *suf);
		}
		while (pos != st.begin()) {
			auto pre = pos; pre--;
			ans -= calc(*pre, last);
			if (a[x] - y <= a[*pre]) {
				last = *pre;
				st.erase(pre);
			} else break;
		}
		a[x] -= y;
		if (x & 1) BIT.modify(x, -y);
		if (*pos == x) {
			if (x != n) {
				auto suf = pos; suf++;
				ans += calc(*pos, *suf);
			}
			if (pos != st.begin()) {
				auto pre = pos; pre--;
				ans += calc(*pre, *pos);
			}
		} else {
			if (a[x] < a[*pos]) {
				st.insert(x);
				ans += calc(x, *pos);
				pos = st.lower_bound(x);
				if (pos != st.begin()) {
					auto pre = pos; pre--;
					ans += calc(*pre, *pos);
				}
			} else if (pos != st.begin()) {
				auto pre = pos; pre--;
				ans += calc(*pre, *pos);
			}
		}
		writeln(getans());
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值