学校里面写这道题的时候洛谷炸了,自己搞出来了。
记当前温度为 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 Q≤2×106 ,将温度离散化。
我们枚举温度 T T T ,找到能取到能量最大值的最大温度,时间复杂度 O ( Q 2 log Q ) O(Q^2 \log Q) O(Q2logQ) 。
答案应该为随 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) 。
类似树状数组找第 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) 。
#include
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();
}
}