大鱼吃小鱼(fhq-treap/线段树二分+贪心)

大鱼吃小鱼

  • description
  • solution
  • code

description

《大鱼吃小鱼》是一款经典的儿童益智类游戏,在游戏中,玩家所操控的“大鱼”只能吃掉体积严格小于自己的“小鱼”,然后玩家所操控的“大鱼”的体积就会增加“小鱼”的体积这么多的量。

知名主播 Bychaha 是《大鱼吃小鱼》这款游戏国服排行榜的前 50 名,为了辅助自己玩这款游戏,Bychaha 研发了一个脚本,该脚本能在游戏开始时快速计算出 Bychaha 需要按照什么顺序吃掉哪些鱼,才能在吃掉最少的鱼的前提下,达到通关所需的体积。在这个强大脚本的辅助下,Bychaha 很快便成功问鼎国服。

很遗憾的是,这一切都是 Bychaha 做的梦,他并不会写代码,所以他想要求助擅长写代码的你来帮助他实现这个脚本。为了降低难度,Bychaha 只需要知道他最少需要吃掉几只鱼才能达到通关的要求。

《大鱼吃小鱼》游戏总共可能发生以下三类事件:

  • l 1 S K 表示一局新游戏开始了,Bychaha 操控的“大鱼”的初始体积为 S,当“大鱼”的体积达到 K 或更大时,这一局游戏通关
  • l 2 W 表示游戏中新加入了一条体积为 W 的鱼
  • l 3 W 表示游戏中某条体积为 W 的鱼消失了,保证在此之前游戏中至少存在一条体积为 W 的鱼

《大鱼吃小鱼》游戏的每一关都是模拟吃鱼的过程,所以在通关之后,被吃掉的鱼并不会从游戏中消失,只有发生(3)号事件时,鱼才会真的消失。

【输入格式】
第一行一个整数 N,表示一开始游戏中存在的鱼的数量。
第二行 N 个数𝑊",表示初始在游戏中的每条鱼的体积。
第三行一个整数 Q,表示事件的数量。
接下来 Q 行,表示发生的事件,格式如题面所述。
【输出格式】
对于每个(1)号事件,输出一个数,表示通关最少需要吃掉几只鱼,如果
无法通关,输出-1。
【样例输入输出】

4 
1 4 8 1 
15 
1 2 3 
1 2 4 
1 2 5 
1 3 3 
1 3 5 
1 3 16 
1 4 16 
1 8 17 
1 100 101 
1 100 115 
1 3 9 
2 2 
1 3 9 
3 4 
1 3 9 
1 
2 
-1 
0 
2 
4 
3 
2 
1 
-1 
3 
2 
-1 

【提示】
游戏过程中 Bychaha 操控的“大鱼”是凭空产生的,在通关或者通关失败之后自动消失。
【数据规模与约定】
对于 10%的数据,𝑁,Q ≤ 10
对于 30%的数据,𝑁,Q ≤ 1000
另有 10%的数据,1 ≤ S,K ≤ 1012,且保证数据完全随机
另有 20%的数据,在任意时刻游戏中至多只有 100 种体积不同的鱼
对于 100%的数据,1 ≤ 𝑁 ≤ 3 ∗ 105,1 ≤ 𝑄 ≤ 105 1 ≤ 𝑊 ≤ 1012,1 ≤ S,K ≤ 1018

solution

第一遍读完题,应该就有很粗暴的思路:贪心地从能吃的最大体积鱼开始吃,一旦体积变大后可以吃新的大鱼,就又去吃新的大鱼,知道到达通关体积为止

有了主干,再修订一下细节枝叶:每次肯定是吃尽量少的鱼,体积刚好可以解锁一种新大鱼就吃新大鱼,如果新大鱼体积不小于通关体积,就只用吃到通关体积即可。

通关体积是大于等于,吃鱼必须严格大于

最后开始寻找工具实现:发现若干问题,想到用二分假模拟,利用线段树维护吃的鱼区间及个数,很不幸这非常难以实现 在经过2h的调试加入新工具再维护后果断得出的结论


实际上,这种看似暴力模拟思路就是正解

直接模拟这个过程,可能会吃nnn次鱼,考虑当前不能吃的最小体积的鱼

显然只有体积严格大鱼这种鱼的体积的时候,可吃鱼集合才会发生改变

如果这种鱼体积大于等于我们的通关体积,就只用吃到通关体积就行了

所以,我们只需要求每一步,对于不同可吃集合的鱼,需要最少吃多少条才能解锁下一步

由于贪心的吃法一定是吃一段连续的鱼(鱼体积是有序的),这个可以二分求出

题解说的线段树二分毁了我好多温柔,还是fhq-treap大法好

接下来是时间复杂度的问题

  • 假设现在我们的体积为AAA,不能吃的鱼体积为BBB,显然A≤BA\le BAB

    我们会吃掉一些鱼xxx,得到A+x>BA+x>BA+x>B

    此时不能吃的鱼体积变为CCC,显然A+x≤CA+x\le CA+xC

    这个时候又再次吃掉一些鱼yyy,得到A+x+y>CA+x+y>CA+x+y>C

    且注意到y≥By\ge ByB,因为解锁可以吃BBB的局面后还不能吃到CCC,需要吃更多的鱼

    所以有A+x+y≥A+x+B>A+B≥A+AA+x+y\ge A+x+B>A+B\ge A+AA+x+yA+x+B>A+BA+A

    也就是说两次二分(两个局面)就至少会翻倍最初体积,时间复杂度自然是log⁡\loglog级别了

最后说说二分的实现

  • 题解说线段树二分,结果造成了2h的无效输出,毁了我好多温柔,果然还是fhq-treap好

    鱼的体积就是点的权值,一条鱼就是一个点,暴力贪心模拟

    首先按现在鱼的体积分裂出能吃的鱼的集合

    然后每一个局面可以知道现在体积与开启下一个局面的差,利用这个按体积和在可吃集合里分裂就能顺便知道使用了多少条鱼

    接着就跳到下一个局面,如果开启不了就是−1-11

    重点是吃了的鱼下一个局面需要直接跳过,我们的操作是合并是将吃的鱼暂时不合并回去,反而是将这个操作记录到栈里面

    也就是说,做到了fhq-treap里面的鱼都是还未被吃掉了

    最后这个询问完成后,一定要回溯操作,把模拟假装吃掉的鱼重新合并回去

code

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define maxn 400005
struct node { int key, val, Min, sum, siz, lson, rson; }t[maxn];
int n, Q, cnt, root, top;
int v[maxn], sta[maxn];int NewNode( int x ) {int now = ++ cnt;t[now].siz = 1;t[now].key = rand();t[now].sum = t[now].Min = t[now].val = x;return now; 
}void pushup( int x ) {t[x].siz = t[t[x].lson].siz + t[t[x].rson].siz + 1;t[x].sum = t[t[x].lson].sum + t[t[x].rson].sum + t[x].val;if( t[x].lson ) t[x].Min = t[t[x].lson].Min;else t[x].Min = t[x].val;
}int build( int l, int r ) {if( l > r ) return 0;int mid = ( l + r ) >> 1;int now = NewNode( v[mid] );t[now].lson = build( l, mid - 1 );t[now].rson = build( mid + 1, r );pushup( now );return now;
}int merge( int x, int y ) {if( ! x or ! y ) return x + y;if( t[x].key < t[y].key ) {t[x].rson = merge( t[x].rson, y );pushup( x );return x;}else {t[y].lson = merge( x, t[y].lson );pushup( y );return y;}
}void split_val( int now, int val, int &x, int &y ) {if( ! now ) x = y = 0;else {if( t[now].val <= val ) x = now, split_val( t[now].rson, val, t[now].rson, y );else y = now, split_val( t[now].lson, val, x, t[now].lson );pushup( now );}
}void split_siz( int now, int siz, int &x, int &y ) {if( ! now ) x = y = 0;else {if( t[t[now].lson].siz >= siz ) y = now, split_siz( t[now].lson, siz, x, t[now].lson );else x = now, split_siz( t[now].rson, siz - t[t[now].lson].siz - 1, t[now].rson, y );pushup( now );}
}void split_sum( int now, int sum, int &x, int &y ) {if( ! now ) x = y = 0;else {if( t[t[now].rson].sum >= sum )  x = now, split_sum( t[now].rson, sum, t[now].rson, y );else y = now, split_sum( t[now].lson, sum - t[t[now].rson].sum - t[now].val, x, t[now].lson );pushup( now );}
}void rebuild( int x, int y ) {root = merge( x, y );while( top ) {split_val( root, t[sta[top]].val, x, y );root = merge( merge( x, sta[top] ), y );top --;}
}void solve( int S, int T ) {int L = 0, R = root, x, y, ans = 0, nxt;while( S < T ) {split_val( R, S - 1, x, y );L = merge( L, x ), R = y;if( ! R ) nxt = T;else nxt = min( T, t[R].Min + 1 );if( t[L].sum + S < nxt ) {printf( "-1\n" );rebuild( L, R );return;}split_sum( L, nxt - S, x, y );ans += t[y].siz, S += t[y].sum;L = x, sta[++ top] = y;}printf( "%lld\n", ans );rebuild( L, R );
}void Insert( int val ) {int x, y;split_val( root, val, x, y );root = merge( merge( x, NewNode( val ) ), y );
}void Delete( int val ) {int x, y, l, r;split_val( root, val, x, y );split_siz( x, t[x].siz - 1, l, r );root = merge( l, y );
}signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &v[i] );sort( v + 1, v + n + 1 );root = build( 1, n );scanf( "%lld", &Q );while( Q -- ) {int opt, s, k, w;scanf( "%lld", &opt );switch( opt ) {case 1 : {scanf( "%lld %lld", &s, &k );solve( s, k );break;}case 2 : {scanf( "%lld", &w );Insert( w );break;}case 3 : {scanf( "%lld", &w );Delete( w );break;}}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316956.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Acwing 1081. 度的数量(以及本人对数位dp的浅薄理解)

题意&#xff1a; 求给定区间 [X,Y] 中满足下列条件的整数个数&#xff1a;这个数恰好等于 K 个互不相等的 B 的整数次幂之和。 题解&#xff1a; 数位DP 技巧1&#xff1a;[X,Y]>f(Y)-f(X-1) 技巧2&#xff1a;用树的方式来考虑。 在本题中&#xff0c;题意是问[X,Y]中…

EFCore动态切换Schema

最近做个分库分表项目&#xff0c;用到schema的切换感觉还是有些坑的&#xff0c;在此分享下。 先简要说下我们的分库分表分库分表规则我定的规则是&#xff0c;订单号&#xff08;数字&#xff09;除以16&#xff0c;得出的结果为这个订单所在的数据库&#xff0c;然后他的余数…

YBTOJ:工作评估(分块)

解析 首先想想 O(nm)O(nm)O(nm) 怎么做。 从左往右扫&#xff0c;不断把当前值和 x0x_0x0​ 取 max⁡\maxmax 即可。 考虑正解&#xff1a; 设 f(l,r,w)f(l,r,w)f(l,r,w) 为初始为 www&#xff0c;工作区间为 (l,r)(l,r)(l,r) 结束后的价值&#xff0c;s(l,r)∑ilrais(l,r)\s…

黑客(续) (压位高精+状压dp)

黑客&#xff08;续&#xff09;descriptionsolutioncodedescription 【问题描述】 在破解了世界首富 Bychaha 的银行账户后&#xff0c;知名黑客 pks 发现&#xff0c;要得到 Bychaha 的全部财产&#xff0c;必须再破解一道密码。 作为客户账户安全的最后一道防线&#xff0…

东莞.NET俱乐部线下技术沙龙-活动报名

自广州.NET技术俱乐部在2018年12月08日线下活动顺利开展后&#xff0c;东莞作为兄弟城市&#xff0c;也想通过线下活动的方式&#xff0c;点燃东莞.NET技术的熊熊之火。现决定先借助广州、深圳兄弟城市的帮助下&#xff0c;开展一场东莞方主办的线下活动&#xff0c;聚集东莞本…

Acwing 1084. 数字游戏 II

Acwing 1084. 数字游戏 II 题意&#xff1a; 指定一个整数闭区间 [a.b]&#xff0c;问这个区间内有多少个取模数。 取模数&#xff1a;这种数字必须满足各位数字之和 mod N 为 0。 题解&#xff1a; 数位dp 这里不细讲数位dp了&#xff0c;可以看看 Acwing 1081. 度的数量&…

ybtoj洛谷P3268:圆的异或并(扫描线)

解析 很神奇的一道题。 关键条件&#xff1a;任意两个圆无交。 把一个圆分成上下两个圆弧&#xff0c;那么所有圆弧的高度关系不会发生变化。 所以可以开一个 set&#xff0c;维护一个从左往右扫的扫描线&#xff0c;按照当前扫描线的横坐标定义比较符号&#xff0c;在圆的最…

如何撰写较受欢迎的技术文章

本来我这篇文章的标题是 “如何撰写受欢迎的技术文章”&#xff0c;但反复斟酌之下&#xff0c;还是加了一个“较”字&#xff0c;这主要是考虑我不是什么知名作者&#xff0c;写的文章大多也谈不上很受欢迎&#xff0c;贸然地谈“受欢迎” 是有点忐忑的&#xff0c;而改成现在…

朝鲜时蔬(分数据点写算法+毒瘤数学)

朝鲜时蔬decriptionsolutioncodedecription 对于一个有穷非空正整数集合S{x1,x2,x3,...,xn}⊂N(n≥1)S\{x_1,x_2,x_3,...,x_n\}\subset N^(n\ge 1)S{x1​,x2​,x3​,...,xn​}⊂N(n≥1)&#xff0c;定义其和sum(S)sum(S)sum(S)为所有元素的和 sum(S)x1...xnsum(S)x_1...x_nsum…

洛谷P7515:矩阵游戏(差分约束)

解析 如果没有元素均要在 [0,1e6][0,1e6][0,1e6] 的条件&#xff0c;可以很容易的构造出一个合法解。 那么我们就要通过调整得到的解&#xff0c;使所有数都在合法范围内。 注意到&#xff0c;每次给某一行/列依次1,-1,1,-1…这样仍然符合要求。 让每一行/列错开&#xff0c;…

Recursive sequence HDU - 5950

Recursive sequence HDU - 5950 题意&#xff1a; 给你一个式子&#xff1a;f[n]2f[n-2]f[n-1]n4 给你f[1]和f[2]&#xff0c;给你一个n&#xff0c;求f[n] f[1],f[2],n<231 题解&#xff1a; 很明显&#xff0c;矩阵快速幂&#xff0c;但是太久没做这种题&#xff0c;我…

使用Http-Repl工具测试ASP.NET Core 2.2中的Web Api项目

今天&#xff0c;Visual Studio中没有内置工具来测试WEB API。使用浏览器&#xff0c;只能测试http GET请求。您需要使用Postman&#xff0c;SoapUI&#xff0c;Fiddler或Swagger等第三方工具来执行WEB API的完整测试。在ASP.NET Core 2.2中&#xff0c;引入了一个名为“http-r…

洛谷P7518:宝石(倍增、可撤销并查集)

解析 算法一 定义 upx,kup_{x,k}upx,k​ 为节点 xxx 从自己的颜色所在位置在返祖链上往后跳 2k2^k2k 个颜色到达的节点。 可以像倍增一样的求解。 这样对于一次询问 (s,t)(s,t)(s,t) 我们就能求出 (s,lca)(s,lca)(s,lca) 这一段能取到哪里了。 对于向下的情况&#xff0c;再处…

E - Counting Cliques HDU - 5952

E - Counting Cliques HDU - 5952 题意&#xff1a; 给你n个点&#xff0c;m个边&#xff0c;还有一个s&#xff0c;问这个图中有多少个等于s的点集可以组成一个完全图 题解&#xff1a; 这题。。直接暴力搜索就行 分析复杂度的时候&#xff0c;应该考虑只有1000条边&#…

Docker最全教程之使用TeamCity来完成内部CI、CD流程(十七)

本篇教程主要讲解基于容器服务搭建TeamCity服务&#xff0c;并且完成内部项目的CI流程配置。教程中也分享了一个简单的CI、CD流程&#xff0c;仅作探讨。不过由于篇幅有限&#xff0c;完整的DevOps&#xff0c;我们后续独立探讨。 为了降低容器的使用门槛以及便于大家将容器技…

2021牛客NOIP提高组第二场T2——方格计数(组合数计数)

方格计数descriptionsolutioncodedescription 在左下角是 (&#x1d7ce;, &#x1d7ce;)&#xff0c;右上角是 (W, H)的网格上&#xff0c;有 (W 1) (H 1) 个格点。 现在要在格点上找 N个不同的点&#xff0c;使得这些点在一条直线上。并且在这条直线上&#xff0c; 相邻…

Acwing 1085. 不要62

Acwing 1085. 不要62 题意&#xff1a; 问[n,m]这些数中有多少数不包含4&#xff0c;且不包含连续的62 题解&#xff1a; 经典数位dp&#xff0c;分析过程以前的数位dp博客有写 Acwing 1082. 数字游戏 代码&#xff1a; #include<bits/stdc.h> #define debug(a,b) …

ybtoj洛谷P4406三角形面积并(扫描线)

解析 暴力求出所有三角形之间的所有交点&#xff0c;提出所有的横坐标。 然后任意两个相邻的横坐标之间的面积都是若干个梯形。 那么就可以求出对于每一个横坐标截得的三角形长度的并的和&#xff0c;然后加在一起乘高除以二即可。 在这里插入代码片#include<bits/stdc.h&…

自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference

在前段时间我写了一篇迁移 csproj 格式的博客 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj&#xff0c;不过全过程是手工进行的&#xff0c;而且到最后处理 XAML 问题也非常头疼。现在&#xff0c;我们可以利用工具自动地完成这个过程。…

[NowCoder牛客]2021NOIP提高组模拟赛第二场T3——树数树(启发式合并堆)

树数树descriptionsolutioncodedescription 【题目描述】 牛牛有一棵 n 个点的有根树&#xff0c;根为 1。 我们称一个长度为 m 的序列 a 是好的&#xff0c;当且仅当&#xff1a; • ∀&#x1d456;∈(1,&#x1d45a;]∀&#x1d456;∈(1, &#x1d45a;]∀i∈(1,m]&#…