洛谷P4135 Ynoi2016 掉进兔子洞 (带权bitset?/bitset优化莫队 模板) 题解
题面。
看到这道题,我第一反应就是莫队。
我甚至也猜出了把所有询问的三个区间压到一起处理然后分别计算对应询问答案。
但是,这么复杂的贡献用什么东西存?难道要开一个数组 query_appear_time[ 100000 ][ 100000 ]?
于是我打消了这个念头,最后还是看题解做的。
简化题意:给一个序列,给一些询问,每个询问包含三个区间代表序列的三个子序列,要求出这三个对应子序列去掉三个子序列都具有的公共数字后剩下的数字个数。
令三个区间为a1,a2,a3。
要求的答案就是a1数字个数-公共数字个数+a2数字个数-公共数字个数+a3数字个数-公共数字个数。
即a1,a2,a3数字个数之和-公共数字个数*3。
数字个数之和就是区间长度,那么问题就转化为了求三个区间的公共数字个数。
对序列进行离散化,原本的1e9值域就变成了1e5值域。
然后读入所有的询问区间按标准莫队进行排序,处理每个区间并且将其所有的数字出现个数转移到对应询问中。接下来合并。
到这一步其实并不难想,关键在于怎么求公共数字个数。
前面我提到显然存不了一个query_appear_time[ 100000 ][ 100000 ],但是有一个数据结构叫bitset。
bitset的一位只占一个比特,相当于一个二进制数,可以进行位运算。
我们可以用bitset一位代表一个数字存储区间内值域,这在 洛谷3674小清新人渣的本愿 中十分有用。
但是这题要存储区间每个数字个数。
做法:
我们令p[i]为整个序列内小于等于i的数字的个数,cnt[i]为数字i在 当前区间 中出现的次数,建一个bitset<1e5>range。
每当读入一个数字x我们令range[p[x]-cnt[x]]为1并令cnt[x]++,每当查询完一个区间我们用这样一个bitset记录这个区间。
代表两个区间的bitset与(&)一下然后用range.count()求出新bitset的1个数,就是这两个区间的公共数字个数。
原理:
如下图
对于一个区间,对于数字i,这个区间的bitset第p[ i ]位到第p[ i ]-cnt[ i ]位为1,公共数字个数就是min(cnt[i])
那么对于两个数的min(cnt[i]),p[i]~p[i]-min(cnt[i])+这个范围肯定全是1.
取与之后,p[ i ]~p[i-1]+1位的1的数量就是公共的i的出现次数。
至于计算每个区间的bitset,这么一大堆区间放在一起,显然可以莫队。
然后分别计算每个区间的bitset,再对每个询问,将其三个区间的bitset与起来求1的个数,就是这个询问所要求的三个区间的公共元素数量。
莫队操作,tmp为当前区间状态。
- for(int i=2;i<=m;i++){
- while(l>q[i].l)
- l--,tmp[p[a[l]]-tim[a[l]]]=1,tim[a[l]]++;
- while(r<q[i].r)
- r++,tmp[p[a[r]]-tim[a[r]]]=1,tim[a[r]]++;
- while(l<q[i].l)
- tim[a[l]]--,tmp[p[a[l]]-tim[a[l]]]=0,l++;
- while(r>q[i].r)
- tim[a[r]]--,tmp[p[a[r]]-tim[a[r]]]=0,r--;
- if(!vis[(q[i].pos-1)/3+1])
- res[(q[i].pos-1)/3+1]=tmp,vis[(q[i].pos-1)/3+1]=1;//神奇的O(1)复制
- else
- res[(q[i].pos-1)/3+1]&=tmp;
- }
合并答案
- for(int i=1;i<=m;i++){
- int pub=res[i].count();
- printf("%d\n",cnt[i]-pub*3);
- }
至此,我们已经得到了一个时间复杂度n3/2的莫队算法。
但是还有一个问题。
bitset虽然小,但是一个bitset要开1e5位。这样的bitset开不了题目要求的1e5个(每个询问要存三个区间)。
那么就只开询问数量的一部分(我开的1/4)的bitset,然后一次处理问题的一小部分就可以了。


- #include<bits/stdc++.h>
- using namespace std;
- const int h=100070;
- int n,m;
- int a[h],line[h];
- int p[h];
- map<int,int>rk;
- int ntot=0;
- bool vis[h/4+10];
- bitset<100010>res[h/4+10];
- bitset<100010>tmp;
- int len;
- int get_pos(int x){
- return (x-1)/len+1;
- }
- struct node{
- int l,r;
- int pos;
- }q[h];
- bool cmp(node x,node y){
- if(get_pos(x.l)==get_pos(y.l))
- return x.r<y.r;
- return x.l<y.l;
- }
- int tim[h];
- int ans;
- int part;
- int cnt[h];
- void solve(){
- if(!part)
- return;
- tmp.reset();
- for(int i=1;i<=part;i++)
- cnt[i]=0,res[i].reset(),vis[i]=0;
- len=sqrt(part);
- for(int i=1;i<=part*3;i++)
- scanf("%d%d",&q[i].l,&q[i].r),q[i].pos=i,cnt[(i-1)/3+1]+=q[i].r-q[i].l+1;
- sort(q+1,q+part*3,cmp);
- int l=q[1].l,r=q[1].r;
- for(int i=l;i<=r;i++)
- tmp[p[a[i]]-tim[a[i]]]=1,tim[a[i]]++;
- vis[(q[1].pos-1)/3+1]=1,res[(q[1].pos-1)/3+1]=tmp;
- for(int i=2;i<=part*3;i++){
- while(l>q[i].l)
- l--,tmp[p[a[l]]-tim[a[l]]]=1,tim[a[l]]++;
- while(r<q[i].r)
- r++,tmp[p[a[r]]-tim[a[r]]]=1,tim[a[r]]++;
- while(l<q[i].l)
- tim[a[l]]--,tmp[p[a[l]]-tim[a[l]]]=0,l++;
- while(r>q[i].r)
- tim[a[r]]--,tmp[p[a[r]]-tim[a[r]]]=0,r--;
- if(!vis[(q[i].pos-1)/3+1])
- res[(q[i].pos-1)/3+1]=tmp,vis[(q[i].pos-1)/3+1]=1;
- else
- res[(q[i].pos-1)/3+1]&=tmp;
- }
- for(int i=1;i<=part;i++){
- int pub=res[i].count();
- printf("%d\n",cnt[i]-pub*3);
- }
- for(int i=1;i<=ntot;i++)
- tim[i]=0;
- }
- int main(){
- scanf("%d%d",&n,&m);
- for(int i=1;i<=n;i++)
- scanf("%d",&a[i]),line[i]=a[i];
- sort(line+1,line+1+n);
- for(int i=1;i<=n;i++)
- if(line[i]!=line[i-1])
- rk[line[i]]=++ntot,p[ntot-1]=i-1;
- p[ntot]=n;
- for(int i=1;i<=n;i++)
- a[i]=rk[a[i]];
- part=h/4;
- while(m)
- part=min(part,m),solve(),m-=part;
- return 0;
- }
完整代码
时间复杂度为m*n1/2,即n3/2,可以通过本题。
很多oier提到Ynoi都只知道充斥着acg的冗长题目背景和严苛的卡常,但是事实上这些题有很多都是很有研究价值的。
说句闲话
lxl的废话之多多少都知道;题面里那游戏我还真玩过,一堆暴力犯罪和恶心而莫名其妙的剧情,我见过这些要素但是真正玩起来还是有一定冲击力的;这是研究算法的地方,不能谈游戏内容,建议mgfs(moegirlwiki first search)
洛谷P4135 Ynoi2016 掉进兔子洞 (带权bitset?/bitset优化莫队 模板) 题解的更多相关文章
- [洛谷P4688][Ynoi2016]掉进兔子洞
题目大意:给定一个$n(n\leqslant10^5)$序列,$m(m\leqslant10^5)$个询问,每个询问给出$l_1,r_1,l_2,r_2,l_3,r_3$.令$s$为该三个区间的交集的 ...
- 洛谷P4689 [Ynoi2016]这是我自己的发明(树上莫队+树链剖分)
题目描述 您正在打galgame,然后突然家长进来了,于是您假装在写数据结构题: 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1.将树根换为 x. 2.给出两个点 x,y,从 ...
- luogu P4688 [Ynoi2016]掉进兔子洞 bitset 莫队
题目链接 luogu P4688 [Ynoi2016]掉进兔子洞 题解 莫队维护bitset区间交个数 代码 // luogu-judger-enable-o2 #include<cmath&g ...
- [Luogu 4688] [Ynoi2016]掉进兔子洞 (莫队+bitset)
[Luogu 4688] [Ynoi2016]掉进兔子洞 (莫队+bitset) 题面 一个长为 n 的序列 a.有 m 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间 ...
- BZOJ.4939.[Ynoi2016]掉进兔子洞(莫队 bitset 分组询问)
BZOJ 洛谷 删掉的数即三个区间数的并,想到bitset:查多个区间的数,想到莫队. 考虑bitset的每一位如何对应每个数的不同出现次数.只要离散化后不去重,每次记录time就可以了. 但是如果对 ...
- Luogu4688 [Ynoi2016]掉进兔子洞 【莫队,bitset】
题目链接:洛谷 我们知道要求的是\([l_1,r_1],[l_2,r_2],[l_3,r_3]\)的可重集取交的大小,肯定是要用bitset的,那怎么做可重集呢? 那就是要稍微动点手脚,首先在离散化的 ...
- BZOJ4939: [Ynoi2016]掉进兔子洞(莫队 bitset)
题意 题目链接 一个长为 n 的序列 a. 有 m 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立. 注意这里删掉指的是一个一个删,不是把等于 ...
- [Ynoi2016]掉进兔子洞 题解
题面传送门:https://www.luogu.org/problemnew/show/P4688 (温馨提示,请直接翻至题目描述部分) 1e5的数据范围,以及对区间每个权值出现次数取min此类主席树 ...
- 洛谷OJ P1196 银河英雄传说(带权并查集)
题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山 ...
随机推荐
- 并发与并行,同步和异步,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang并发编程之GoroutineEP13
如果说Go lang是静态语言中的皇冠,那么,Goroutine就是并发编程方式中的钻石.Goroutine是Go语言设计体系中最核心的精华,它非常轻量,一个 Goroutine 只占几 KB,并且这 ...
- Python入门系列(六)一篇学会python函数
函数 函数是只在调用时运行的代码块. def my_function(): print("Hello from a function") my_function() 信息可以作为参 ...
- 开源:Taurus.MVC-Java 版本框架 (支持javax.servlet.*和jakarta.servlet.*双系列,内集成微服务客户端)
版本说明: 因为之前有了Taurus.MVC-DotNet 版本框架,因此框架标了-Java后缀. .Net 版本: 开源文章:开源:Taurus.MVC-DotNet 版本框架 (支持.NET C ...
- KingbaseES V8R6C5B041 sys_backup.sh单实例备份案例
数据库版本: test=# select version(); version ---------------------------------------------------------- ...
- 让Python更优雅更易读(第二集)
友情链接 让Python更优雅更易读(第一集) 1.装饰器 1.1装饰器特别适合用来实现以下功能 运行时校验:在执行阶段进行特定校验,当校验通不过时终止执行. 适合原因:装饰器可以方便地在函数执行前介 ...
- 1.关于433MHz按键单片机解码
近段时间做项目要用到单片机接收433MHz按键发过来的码值,涉及短按.连按.长按,由于之前没有做过这方面一开始有点蒙,找遍网上都没有案例,现在项目完成了整理自己的一些心得和大家分享分享!!!直入主题. ...
- Linux下自动删除过期备份和自动异地备份
每天自动删除过期备份 首先编写一个简单的Shell脚本DeleteExpireBackup.sh: #!/bin/bash # 修改需要删除的路径 location="/database/b ...
- Python数据科学手册-Numpy数组的计算,通用函数
Python的默认实现(CPython)处理某些操作非常慢,因为动态性和解释性, CPython 在每次循环必须左数据类型的检查和函数的调度..在编译是进行这样的操作.就会加快执行速度. 通用函数介绍 ...
- 通过vNode实现给列表字段打标签
问题 如何给列表数据打标签?类似下面这种样子 思路 数模转化(对接口请求回来的数据进行过滤标记,返回新的数据) 渲染新的数据模型 实现 1.过滤数据,需要打标签的采用jsx写法 业务数据的处理我封装在 ...
- 现有rabbitmq集群添加新节点,移除旧节点(可以作为rabbitmq集群迁移使用)
原有集群安装步骤:https://www.cnblogs.com/sanduzxcvbnm/p/15797788.html 1.拉取镜像 集群中新节点需要执行 docker pull rabbitmq ...