[ 联合省选 2020 A | B ] 冰火战士 题解

题目

学校里面写这道题的时候洛谷炸了,自己搞出来了。

Part 0

记当前温度为 T T T ,小于等于 T T T 火战士能量和为 f 1 ( T ) f1(T) f1(T) , 大于等于 T T T 冰战士能量和为 f 2 ( T ) f2(T) f2(T)

由于能量有剩余的战士会一直战斗下去,此温度下的能量和应该为 2 × min ⁡ ( f 1 ( T ) , f 2 ( T ) ) 2\times\min (f1(T),f2(T)) 2×min(f1(T),f2(T))

而对于 f 1 ( T ) , f 2 ( T ) f1(T),f2(T) f1(T),f2(T) 的计算,我们将温度作为下标,即为对于温度 T T T 的前缀和 / 后缀和,考虑使用树状数组。

注意到温度 ≤ 1 0 9 \le 10^9 109 Q ≤ 2 × 1 0 6 Q \le 2\times 10^6 Q2×106 ,将温度离散化。

Part 1

我们枚举温度 T T T ,找到能取到能量最大值的最大温度,时间复杂度 O ( Q 2 log ⁡ Q ) O(Q^2 \log Q) O(Q2logQ)

Part 2

答案应该为随 T T T 的增大的单峰函数,但是由于最大值可能有很多个,三分法不方便实现。

f 1 ( T ) , f 2 ( T ) f1(T),f2(T) f1(T),f2(T) 应该随着 T T T 的增大而分别单调增和单调减的,为了使两者中最小值最大,答案应该在交点处取到,而考虑到温度的离散,答案不一定能取到交点。

我们可以先二分找出 最后一个 满足 f 1 ( T ) < f 2 ( T ) f1(T) < f2(T) f1(T)<f2(T) T T T 记为 k 1 k1 k1 k 1 k1 k1 即为最小值为 f 1 f1 f1 时的最优解。而 k 1 k1 k1 是最后一个,所以 f 1 ( k 1 + 1 ) ≥ f 2 ( k 1 + 1 ) f1(k1+1) \ge f2(k1+1) f1(k1+1)f2(k1+1) ,此时 f 2 f2 f2 作为最小值,找最后一个温度 k 2 k2 k2 使得 f 2 ( k 1 + 1 ) = f 2 ( k 2 ) f2(k1+1)=f2(k2) f2(k1+1)=f2(k2) ,此时 k 2 k2 k2 即为最小值为 f 2 f2 f2 的最优解。

此时时间复杂度 ( Q log ⁡ 2 Q ) (Q \log ^2 Q) (Qlog2Q)

Part 3

类似树状数组找第 k k k 大,由于树状数组中 t r e e [ x ] tree[x] tree[x] 管辖的区域是 l o w b i t ( x ) lowbit(x) lowbit(x) ,那么从 0 0 0 开始,每次加 2 i 2^i 2i ,累计的和一定表示从 1 1 1 开始的连续区间。我们根据这一特性在树状数组上倍增找出 k 1 , k 2 k1,k2 k1,k2 ,这一部分就从原来的 log ⁡ 2 Q \log^2Q log2Q 优化到了 log ⁡ Q \log Q logQ

此时时间复杂度 O ( Q log ⁡ Q ) O(Q \log Q) O(QlogQ)

Code

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 5;
int Q;
struct Question{
	int opt, t, x, y, k;
}q[MAXN];
int sum2, cnt1, cnt2, tem[MAXN], tot;
int tree1[MAXN], tree2[MAXN];
int lowbit(int x) {return x & (-x);}
void add(int *tree, int x, int d)
{
	for(int i=x; i<=tot; i+=lowbit(i))
		tree[i] += d;
}
int query(int *tree, int x)
{
	int res = 0;
	for(int i=x; i>0; i-=lowbit(i))
		res += tree[i];
	return res;
}
void solve()
{
	int k1 = 0, k2 = 0, pos = 0, cur1 = 0, cur2 = sum2;
	for(int i=21; i>=0; i--)
	{
		int new_pos = pos + (1 << i); 
		if(new_pos > tot) continue;
		int new_cur1 = cur1 + tree1[new_pos], new_cur2 = cur2 - tree2[new_pos];
		if(new_cur1 < new_cur2) k1 = new_pos, pos = new_pos, cur1 = new_cur1, cur2 = new_cur2;
	}
	
	int target = sum2 - query(tree2, k1 + 1);
	k2 = 0, pos = 0, cur2 = sum2;
	for(int i=21; i>=0; i--)
	{
		int new_pos = pos + (1 << i); 
		if(new_pos > tot) continue;
		int new_cur2 = cur2 - tree2[new_pos];
		if(new_cur2 > target) pos = new_pos, cur2 = new_cur2;
		if(new_cur2 == target ) k2 = new_pos, pos = new_pos, cur2 = new_cur2;
	}
	
	int ans = 0, anst = 0;
	ans = max(cur1, cur2);
	if(cur1 == cur2) anst = max(k1, k2);
	else anst = (cur1 > cur2) ? k1 : k2;
	
	if(ans == 0) cout<< "Peace\n";
	else cout << tem[anst] << " " << (ans<<1) << "\n";
}
signed main()
{
	std::ios::sync_with_stdio(false); std::cin.tie(0);
	cin >> Q;
	for(int i=1; i<=Q; i++)
	{
		cin >> q[i].opt;
		if(q[i].opt == 1) cin >> q[i].t >> q[i].x >> q[i].y, tem[++tot] = q[i].x;
		if(q[i].opt == 2) cin >> q[i].k;
	}
	
	sort(tem+1, tem+tot+1);
	
	tot = unique(tem+1, tem+tot+1) - tem - 1;
	for(int i=1; i<=Q; i++)
	{
		if(q[i].opt == 1) 
		{
			q[i].x = lower_bound(tem+1, tem+tot+1, q[i].x) - tem;
			if(q[i].t == 0) add(tree1, q[i].x, q[i].y);
			if(q[i].t == 1) add(tree2, q[i].x + 1, q[i].y), sum2 += q[i].y;
		}
		if(q[i].opt == 2)
		{
			int id = q[i].k;
			if(q[id].t == 0) add(tree1, q[id].x, -q[id].y);
			if(q[id].t == 1) add(tree2, q[id].x + 1, -q[id].y), sum2 -= q[id].y;
		}
		solve();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值