[省选联考 2020 A/B 卷] 冰火战士(树状数组上二分)
文章目录
- problem
- solution(10pts)
- code(10pts)
- solution(30pts)
- code(30pts)
- solution(60pts)
- code(60pts)
- solution(100pts)
- code(100pts)
problem
luogu-P6619
一场比赛即将开始。
每位战士有两个属性:温度和能量。
有两派战士:
- 冰系战士的技能会对周围造成降温冰冻伤害,因而要求场地温度不低于他的自身温度才能参赛;
- 火系战士的技能会对周围造成升温灼烧伤害,因而要求场地温度不高于他的自身温度才能参赛。
当场地温度确定时,双方能够参赛的战士分别排成一队。
冰系战士按自身温度从低到高排序;火系战士按自身温度从高到低排序;温度相同时能量大的战士排在前面。
首先,双方的第一位战士之间展开战斗,两位战士消耗相同的能量,能量少的战士将耗尽能量退出比赛,而能量有剩余的战士将继续和对方的下一位战士战斗(能量都耗尽则双方下一位战士之间展开战斗)。
如此循环,直至某方战士队列为空,比赛结束。
你需要寻找最佳场地温度:使冰火双方消耗总能量最高的温度的最高值。
现在,比赛还处于报名阶段,目前还没有任何战士报名,接下来你将不断地收到报名信息和撤回信息。
其中,报名信息包含报名战士的派系和两个属性,撤回信息包含要撤回的报名信息的序号。
每当报名情况发生变化(即收到一条信息)时,你需要立即报出当前局面下的最佳场地温度,以及该场地温度下双方消耗的总能量之和是多少。
若当前局面下无论何种温度都无法开展比赛(某一方没有战士能参赛),则只要输出 Peace
。
solution(10pts)
- Q≤100,x≤103Q\le 100,x\le 10^3Q≤100,x≤103。
枚举整个信息读入过程,O(Q)O(Q)O(Q)。
然后枚举答案温度,O(T)O(T)O(T)。
再计算当前温度下报名的且可以参赛的冰系战士/火系战士的总能量,O(Q)O(Q)O(Q)。
取所有温度下二者的较小值的最大值,输出其的两倍及记录的温度。
时间复杂度 O(Q2T)O(Q^2T)O(Q2T) ,其中 TTT 是所有战士的温度最大值。
可以获得 10′10'10′ 的普通成绩。
code(10pts)
namespace subtask1 {bool vis[105];void solve() {for( int i = 1, op, k, x, y;i <= Q;i ++ ) {scanf( "%d %d", &op, &k );if( op & 1 ) {scanf( "%d %d", &x, &y );g[i] = { k, x, y };vis[i] = 1;}else vis[k] = -1;int ans = 0, ti;for( int T = 1;T <= 1e3;T ++ ) {int ice = 0, fire = 0;for( int j = 1;j <= i;j ++ )if( vis[j] == 1 ) {if( g[j].k == 0 and g[j].x <= T ) ice += g[j].y;if( g[j].k == 1 and g[j].x >= T ) fire += g[j].y;}if( ans < min( ice, fire ) ) ans = min( ice, fire ), ti = T;else if( ans == min( ice, fire ) ) ti = max( ti, T );}if( ! ans ) puts( "Peace" );else printf( "%d %d\n", ti, ans << 1 );}}
}
solution(30pts)
- Q≤104,x≤5000Q\le 10^4,x\le 5000Q≤104,x≤5000,不存在撤回信息,且输入的 xxx 按顺序不降。
每次枚举温度再枚举战士计算的原始暴力就不可行了。
关注特殊的两个性质:
不存在撤回信息。
我们原始暴力为什么要枚举战士,不仅仅是为了计算战士的能量、温度,更重要的是我们不知道当时某个战士是否已经撤回了报名信息。
如果不撤回,我们就期望能找到一个数据结构或者什么工具来维护这些战士的能量和。
输入的 xxx 按顺序不降。
已知冰系战士的温度从小到大排序,火系战士的温度从大到小排序。
所以每次加入战士,如果是冰系就排在冰系队伍的最后面;如果是火系就排在火系队伍的最前面。
我们可以用两个数组来模拟这个过程,用指针移动即可。
且当两个战士同派系且温度一样的话,就没有什么区别了,所以直接合并成一个战士。
最后考虑计算答案。
从高到低枚举参与比赛的温度最低的火系战士,即当时的场地温度。(证明见满分题解)
则冰系战士能参与比赛的一定对应的是其队伍的一段前缀。
而随着枚举的火系战士温度降低,冰系战士能参加比赛的数量也在减少。
所有可以用指针枚举能参与比赛的温度最高的冰系战士。
时间复杂度 O(Q2)O(Q^2)O(Q2)。结合前一档,可以获得 30′30'30′ 的不错成绩。
code(30pts)
namespace subtask1 {}
namespace subtask2 {pair < int, int > ice[10005], fire[10005];void solve() {int p0 = 0, p1 = Q + 1, sum_ice = 0;for( int i = 1, op, k, x, y;i <= Q;i ++ ) {scanf( "%d %d %d %d", &op, &k, &x, &y );if( k == 0 ) {sum_ice += y;if( p0 and ice[p0].first == x ) ice[p0].second += y;else ice[++ p0] = make_pair( x, y );}else {if( p1 <= Q and fire[p1].first == x ) fire[p1].second += y;else fire[-- p1] = make_pair( x, y );}int p = p0, Sice = sum_ice, Sfire = 0, ans = 0, ti;for( int j = p1;j <= Q;j ++ ) {int T = fire[j].first; Sfire += fire[j].second;while( T < ice[p].first ) Sice -= ice[p --].second;if( ans < min( Sice, Sfire ) ) ans = min( Sice, Sfire ), ti = T;if( ans == min( Sice, Sfire ) ) ti = max( ti, T );}if( ! ans ) puts("Peace");else printf( "%d %d\n", ti, ans << 1 );}}
}
solution(60pts)
- Q≤2e5,x≤2e5Q\le 2e5,x\le 2e5Q≤2e5,x≤2e5。
再次注意到,冰系战士是按温度从低到高,火系战士是按温度从高到低。
即冰系战士不降,火系战士不升。
而冰系战士参加比赛人数越多(前缀取得越多),火系战士就越少;反之火系越多,冰系越少。
所以我们大胆猜测可以三分?快速求前缀能量和,利用树状数组/线段树均可。
实测写了后,很遗憾,消耗总能量最高都求不对。输出中间三分点,发现存在平台。
仔细想想,确实很容易就出现平台,因为战士的温度是离散的。
平台三分不行。我们考虑二分。
如果火系战士的总能量更大,就再升高一下场次温度;反之降温。(这其实是正解的做差后的二分思想了)
写了后,又哭又笑,消耗总能量最高求对了。但是场次温度并不是最大值。
同样是因为温度的离散问题。
但是我们肯定温度一定是某个战士的自身温度。
所以直接暴力一点,去找比记录温度高的战士温度,然后计算答案是否和总能量一样,一样就记录这个温度为场地温度,继续找;知道总能量消耗小于答案为止。
所以还要用个 set\text{set}set 来记录所有报名的战士的温度。
时间复杂度 O(Qlog2T)O(Q\log^2 T)O(Qlog2T)(因为那个暴力也跳不了多少次,复杂度前面最多乘个几的常数)。
结合前两档的部分,可以获得 60′60'60′ 的优秀成绩。
code(60pts)
namespace subtask1 {}
namespace subtask2 {}
namespace subtask3 {#define maxn 200005#define lson now << 1#define rson now << 1 | 1#define mid (l + r >> 1)set < pair < int, int > > val;namespace ice {int sum[maxn << 2];void modify( int p, int x, int now = 1, int l = 1, int r = 2e5 ) {if( l == r ) { sum[now] += x; return; }if( p <= mid ) modify( p, x, lson, l, mid );else modify( p, x, rson, mid + 1, r );sum[now] = sum[lson] + sum[rson];}int query( int L, int R, int now = 1, int l = 1, int r = 2e5 ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return sum[now];return query( L, R, lson, l, mid ) + query( L, R, rson, mid + 1, r );}}namespace fire {int sum[maxn << 2];void modify( int p, int x, int now = 1, int l = 1, int r = 2e5 ) {if( l == r ) { sum[now] += x; return; }if( p <= mid ) modify( p, x, lson, l, mid );else modify( p, x, rson, mid + 1, r );sum[now] = sum[lson] + sum[rson];}int query( int L, int R, int now = 1, int l = 1, int r = 2e5 ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return sum[now];return query( L, R, lson, l, mid ) + query( L, R, rson, mid + 1, r );}}int find( int x, int ans ) {while( 1 ) {auto it = val.upper_bound( make_pair( x, Q ) );if( it == val.end() ) return x;int sum1 = fire :: query( it->first, 2e5 );int sum2 = ice :: query( 1, it->first );if( ans > min( sum1, sum2 ) ) return x;else x = it->first;}}void solve() {int Min = 1e9, Max = -1e9;for( int i = 1, op, k, x, y;i <= Q;i ++ ) {scanf( "%d %d", &op, &k );if( op & 1 ) {scanf( "%d %d", &x, &y );val.insert( make_pair( x, i ) );Min = min( Min, x );Max = max( Max, x );g[i] = { k, x, y };if( k == 0 ) ice :: modify( x, y );else fire :: modify( x, y );}else {val.erase( make_pair( g[k].x, k ) );if( g[k].k == 0 ) ice :: modify( g[k].x, - g[k].y );else fire :: modify( g[k].x, - g[k].y );}int l = 1, r = 2e5;int ans = 0, ti;while( l <= r ) {int sum1 = fire :: query( mid, 2e5 );int sum2 = ice :: query( 1, mid );if( ans < min( sum1, sum2 ) ) ans = min( sum1, sum2 ), ti = mid;else if( ans == min( sum1, sum2 ) ) ti = mid;if( sum1 >= sum2 ) l = mid + 1;else r = mid - 1;}if( ! ans ) puts("Peace");else printf( "%d %d\n", find( ti, ans ), ans << 1 );}}
}
solution(100pts)
- Q≤2e6,1≤x≤2e9Q\le 2e6,1\le x\le 2e9Q≤2e6,1≤x≤2e9。
observation1:\text{observation1}:observation1: 冰系战士排列后温度成不降函数;火系战士排列后温度成不升函数。
observation2:\text{observation2}:observation2: 最后的场地温度一定是某个战士的体温。
证明:显然。感性感知一下。
如果最后的场地温度不是某个战士的温度。
那么这个场地温度一定 <<< 参与比赛的火系战士的最低温度,>>> 参与比赛的冰系战士的最高温度。
这个场地温度就可以被调整为 === 参与比赛的火系战士的最低温度。
这样火系和冰系战士原本能参与比赛的没有变化,说不定还可以新增几名参与比赛的冰系战士。
温度拉高,总消耗也没有变劣。
基于这个结论,我们可以将战士的温度离散化成和 QQQ 同量级。
假设场地温度为 TTT,能参加比赛的冰系战士的能量和为 I(T)\text{I}(T)I(T),能参加比赛的火系战士的能量和为 F(T)\text{F}(T)F(T)。
勾勒成函数后,一个单增(火),一个单减(冰)。答案肯定是在交点处。
而我们真正要二分的应该两函数合并取较小值的函数。
更详细地,我们记 Ice(i):\text{Ice}(i):Ice(i): 自身温度 ≤i\le i≤i 的冰系战士的能量和,记 Fire(i):\text{Fire}(i):Fire(i): 自身温度 ≤i\le i≤i 的火系战士的能量和,记当前报名的所有火系战士的能量和为 sumsumsum。
则当场地温度为 TTT 的时候,答案即为 min(Ice(T),sum−Fire(T−1))\min\Big(\text{Ice}(T),sum-\text{Fire}(T-1)\Big)min(Ice(T),sum−Fire(T−1)) 再乘 222。
我们二分的函数应该是两个前缀和函数做差:G(T)=Ice(T)−(sum−Fire(T−1))G(T)=\text{Ice}(T)-(sum-\text{Fire}(T-1))G(T)=Ice(T)−(sum−Fire(T−1))。(与上图不太一样,反转了火系函数)
因为 Ice(T)\text{Ice}(T)Ice(T) 函数不降,sum−(Fire(T−1))sum-(\text{Fire}(T-1))sum−(Fire(T−1)) 函数不升,所以 G(T)G(T)G(T) 函数非严格不降。
那么交点必定在最后一个 ≤0\le 0≤0 和第一个 >0>0>0 的位置之间。
所以我们二分最后一个使得 G(T)≤0G(T)\le 0G(T)≤0 的位置 ttt,答案必定在 t/t+1t/t+1t/t+1 处取得。
如果在 ttt 处答案更大,则温度自然就是 ttt;
否则还要在函数平台处二分出最大的温度 t0t_0t0,满足 Ice(t+1)−(sum−Fire(t))=Ice(t0)−(sum−Fire(t0−1))⇒Fire(t)≥Fire(t0−1)\text{Ice}(t+1)-(sum-\text{Fire}(t))=\text{Ice}(t_0)-(sum-\text{Fire}(t_0-1))\Rightarrow \text{Fire}(t)\ge \text{Fire}(t_0-1)Ice(t+1)−(sum−Fire(t))=Ice(t0)−(sum−Fire(t0−1))⇒Fire(t)≥Fire(t0−1)。
但肯定不能二分后树状数组查询,这样跟 60′60'60′ 的做法是一样的。(推导过程严谨了一些而已,但谁管这个呢?》》
这里我们要用到 树状数组上二分,与线段树上二分类似,是 O(nlogn)O(n\log n)O(nlogn) 的。
为什么不用线段树二分,是因为这位虽然看似一个 log\loglog 但是跑出来因为常数问题,跟二分后树状数组差不多。
- 树状数组二分。
这需要改写一下上面函数的形式:
记 fire(i):fire(i):fire(i): 温度为 iii 的火系战士的能量和。
Ice(T)−(sum−Fire(T−1))≤0⇒Ice(T)+Fire(T)−fire(T)≤sum\text{Ice}(T)-(sum-\text{Fire}(T-1))\le 0\Rightarrow \text{Ice(T)}+\text{Fire}(T)-fire(T)\le sumIce(T)−(sum−Fire(T−1))≤0⇒Ice(T)+Fire(T)−fire(T)≤sum。
这样下标只有 TTT 且具有单调性,可以树状数组二分了。
Fire(T)−fire(T)≤sum−ans\text{Fire}(T)-fire(T)\le sum-ansFire(T)−fire(T)≤sum−ans。
时间复杂度 O(QlogQ)O(Q\log Q)O(QlogQ),可以获得 100′100'100′ 的好成绩。
code(100pts)
#include <bits/stdc++.h>
using namespace std;
#define maxn 2000005
int val[maxn], fire[maxn];
int Q, n, cnt_fire, sum_fire, cnt_ice;
struct node { int op, k, x, y; }q[maxn];
struct BIT {int sum[maxn];void modify( int i, int x ) {for( ;i <= n;i += i & -i ) sum[i] += x;}int query( int i ) {int ans = 0;for( ;i;i -= i & -i ) ans += sum[i];return ans;}
}Fire, Ice;int calc( int x ) {return min( Ice.query( x ), sum_fire - Fire.query( x - 1 ) );
}int main() {scanf( "%d", &Q );for( int i = 1;i <= Q;i ++ ) {scanf( "%d %d", &q[i].op, &q[i].k );if( q[i].op & 1 ) scanf( "%d %d", &q[i].x, &q[i].y ), val[++ n] = q[i].x;}sort( val + 1, val + n + 1 );n = unique( val + 1, val + n + 1 ) - val - 1;for( int i = 1;i <= Q;i ++ )if( q[i].op & 1 ) q[i].x = lower_bound( val + 1, val + n + 1, q[i].x ) - val;for( int t = 1;t <= Q;t ++ ) {int k = q[t].k, x, y;if( q[t].op & 1 ) {x = q[t].x, y = q[t].y;if( k == 0 ) Ice.modify( x, y ), cnt_ice ++;else Fire.modify( x, y ), cnt_fire ++, sum_fire += y, fire[x] += y; }else {x = q[k].x, y = q[k].y;if( q[k].k == 0 ) Ice.modify( x, -y ), cnt_ice --;else Fire.modify( x, -y ), cnt_fire --, sum_fire -= y, fire[x] -= y;}int p = 0, ans = 0;for( int i = 20;~ i;i -- ) {k = p | (1 << i);if( k <= n and ans + Ice.sum[k] + Fire.sum[k] - fire[k] <= sum_fire )p |= (1 << i), ans += Ice.sum[k] + Fire.sum[k];}int ans1 = calc( p ), ans2 = calc( p + 1 );if( max( ans1, ans2 ) <= 0 ) { puts("Peace"); continue; }if( ans1 > ans2 ) { printf( "%d %d\n", val[p], ans1 << 1 ); continue; }p = 0, ans = 0;for( int i = 20;~ i;i -- ) {k = p | (1 << i);if( k <= n and ans + Fire.sum[k] - fire[k] <= sum_fire - ans2 )p |= (1 << i), ans += Fire.sum[k];/*ans2说明火系战士的能量和更小二分平台上的最大温度t0在前缀和Fire数组中以T为自变量呈一个平台满足 Fire(t)>=Fire(t0-1)即 sum-Fire(t)<=sum-Fire(t0-1)*/}printf( "%d %d\n", val[p], ans2 << 1 );}return 0;
}
[省选联考 2020 A/B 卷] 冰火战士(树状数组上二分)相关推荐
- [省选联考 2020 A/B 卷] 信号传递(状压dp + 卡空间)
problem luogu-P6622 一条道路上从左至右排列着 mmm 个信号站,初始时从左至右依次编号为 1,2,-,m1,2,\dots,m1,2,-,m,相邻信号站之间相隔 111 单位长度. ...
- P6620 [省选联考 2020 A 卷] 组合数问题(斯特林数、下降幂)
解析 给出 n,x,pn,x,pn,x,p 和一个 mmm 次的多项式 f(k)f(k)f(k),求解: ∑k=0nf(k)xk(nk)modp\sum_{k=0}^nf(k)x^k\binom n ...
- P6628-[省选联考 2020 B 卷] 丁香之路【欧拉回路,最小生成树】
正题 题目链接:https://www.luogu.com.cn/problem/P6628 题目大意 给出nnn个点的一张完全无向图,i∼ji\sim ji∼j的边权是∣i−j∣|i-j|∣i−j∣ ...
- 省选联考 2020 A 卷
省赛虽然炸了,但题解还是要写滴(笑) 第 200 篇 b l o g 祭 ! ! \Huge 第200篇blog祭!! 第200篇blog祭!! 组合数问题 题意简单明了 首先要看出要把多项式的每一项 ...
- [省选联考 2020 B 卷] 卡牌游戏 题解c++
看到这题,真的忒简单啊! 咳咳,开个玩笑.关于这题: 题目描述 轩轩某天想到了一个卡牌游戏,游戏规则如下: 初始时轩轩的手中有自左向右排成一排的 n 张卡牌,每张卡牌上有一个整数分值. 接下来,轩轩每 ...
- P7516 [省选联考 2021 A/B 卷] 图函数
解析 纯纯的人类智慧题. 关键性质:vvv 可以在计算 f(u,G)f(u,G)f(u,G) 时产生贡献,当且仅当 GGG 中 u,vu,vu,v 之间可以通过 [v,n][v,n][v,n] 的点互 ...
- P7519-[省选联考 2021 A/B 卷]滚榜【状压dp】
正题 题目链接:https://www.luogu.com.cn/problem/P7519 题目大意 nnn个队伍,队伍之间按照得分从小到大排名,得分相同的按照编号从小到大排.开始时每个队伍有个初始 ...
- P6619-[省选联考2020A/B卷]冰火战士【树状数组二分】
正题 题目链接:https://www.luogu.com.cn/problem/P6619 题目大意 有火系战士和冰系战士有一个温度和一个战斗力,每次加入或删除一个战士,要求一个最大的kkk使得温度 ...
- 2009成渝微型计算机处于空白,学海园大联考 2020届高三信息卷(二)文综答案
学海园大联考 2020届高三信息卷(二)文综答案 需要核对本张试卷答案请点击页面底部"立即查看" 更多试卷易对试卷答案核对请微信公众号搜索"答案易对网"关注! ...
最新文章
- 互联互通谋定贸易伙伴 国际农民丰收节贸易会品牌发展
- 原创 | 微服务网关 Kong 科普
- 性能测试 vs 负载测试 vs 压力测试
- java线程基础_Java多线程基础
- python中if函数的使用方法_(for i in range) (else) (if)使用方法
- 作为“梅西式”程序员,我要跳槽了
- 注意!思科Aironet 1830和1850系列存在硬编码密码,请尽快修复!
- ios3怎么取消长按弹出菜单_针对数码打印机中叠印怎么处理
- vb.net中如何结束一个线程
- js字体溢出字体变小_可变字体:它们是什么,以及如何使用它们
- python操作mysql批量插入
- 什么是软件安全性测试?安全测试有哪些测试方法和手段
- php导出word 模板,Laravel+phpword导出word
- 听力1-10中的不熟悉的单词
- 【Python零基础到入门】Python基础语法篇——基本数据类型【文末送书】
- 杀不死的人狼——我读《人月神话》(一)
- 日常瘦脸8个关键细节
- python基础学习2020.6.23-条件、循环和其他语句
- 水声功率放大器应用:潜艇水下通信方式
- 转换插头SAA认证/CE认证/PSE认证/IEC报告/BS报告等常见认证测试标准
热门文章
- 一晚啪了5只喵,累到在医院打点滴,这中国喵把英国人看傻了 | 今日最佳
- 全球孩子迷恋手机/iPad,其实罪魁祸首是父母!
- 神经网络简史:BP算法后的又一突破—信念网络
- 我们只知大势将至,却不知未来已来
- 计算机视觉招聘_INDEMIND|SLAM、计算机视觉、深度学习算法招聘(社招实习)
- idea 配置jdk版本_JDK 11 安装过程(同时已安装了JDK 8)以及Intellij IDEA 配置
- 服务器运行慢都有哪些问题,服务器数据库的运行速度很慢问题
- io流图解 java_详细讲解JAVA中的IO流
- linux 手机 wlan信号桥,手机WLAN信号桥是什么?WLAN信号的作用和使用方法
- java面试题_阿里大厂流出的数百道 Java 经典面试题