首页 > 其他分享 >P4688 [Ynoi2016] 掉进兔子洞

P4688 [Ynoi2016] 掉进兔子洞

时间:2023-04-01 16:22:42浏览次数:44  
标签:ch int sum 兔子 RE while Ynoi2016 now P4688

RE了大约12次以后,SoN3ri告诉我是bitset开小了。

image

那你为什么全RE了啊(?

题意是给你一个长度为 \(n\) 的序列,一共 \(m\) 次询问,每次询问包含三个区间,求三个区间内相同的数去掉后剩下的数的个数。

做完了小清新人渣的本愿,看啥都是bitset+莫队,这题我也是一开始打了一个莫队+bitset,但是很不幸我因为开小了RE了12次,但这都不重要了,来看一下如何做这道题目。

首先看到值域很大,所以我们先离散化,然后对于每一个询问,我们都开一个bitset sum[N],来存放每一次询问中重复的元素的个数,我们发现题目解释里面举得例子,要求不是每一个区间去重后再进行查询,也就是说,我们不能在离散化的时候去重,这样我们在添加和删除一个值对答案的贡献的时候,我们就可以直接用第 x+cnt[x] 位来表示当前点是否重合。在进行对于答案的计算时,我们假设一开始所有的数都是不重合的,然后把 sum[i] 给全赋成1(因为后面是要进行取与运算的),表示一开始都重合,然后三个区间和 sum[i] 取与,得到的里面1的个数就是重合的元素的个数。

然后我们就可以写出下面的代码:

#include<bits/stdc++.h>
#define N 100100
using namespace std;
int n,m,kc,a[N],bl[N],ans[N],cnt[N];
bitset<N>sum[N],now;
struct sb{int l,r,id;}e[N];
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}
inline int cmp(sb a,sb b){if(bl[a.l]==bl[b.l])return a.r<b.r;return a.l<b.l;}
inline void add(int x){now.set(x+cnt[x]);cnt[x]++;}
inline void del(int x){cnt[x]--;now.reset(x+cnt[x]);}
signed main()
{
	n=read();m=read();
	kc=sqrt(n);
	for(int i=1;i<=n;i++)
	  a[i]=read(),bl[i]=(i-1)/kc+1;
	int tmp[N];
	memcpy(tmp,a,sizeof(a));
	sort(tmp+1,tmp+n+1);
	for(int i=1;i<=n;i++)
	  a[i]=lower_bound(tmp+1,tmp+n+1,a[i])-tmp;
	int tot=0;
	now.reset();
	for(int i=1;i<=m;i++)
	{
		ans[i]=0;
		sum[i].set();
		for(int j=1;j<=3;j++)
		{
			e[++tot].l=read();e[tot].r=read();
			e[tot].id=i;
			ans[i]+=e[tot].r-e[tot].l+1;
		}
	}
	sort(e+1,e+tot+1,cmp);
	int nl=1,nr=0;
	for(int i=1;i<=tot;i++)
	{
		while(nl>e[i].l)add(a[--nl]);
		while(nr<e[i].r)add(a[++nr]);
		while(nl<e[i].l)del(a[nl++]);
		while(nr>e[i].r)del(a[nr--]);
		sum[e[i].id]&=now;
	}
	for(int i=1;i<=m;i++)
		cout<<ans[i]-(int)sum[i].count()*3<<endl;
	return 0;
}

然后我就从RE变成了:
image

开这么多不MLE才怪。。。。

然后稍加思索,我们可以把询问“分块”处理,把他分成几组小的分别处理就好了。
时限三秒放心memset。

#include<bits/stdc++.h>
#define N 100100
#define M 25100
using namespace std;
int n,m,kc,a[N],bl[N],ans[N],cnt[N];
bitset<N>sum[M],now;
struct sb{int l,r,id;}e[N];
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}
inline int cmp(sb a,sb b){if(bl[a.l]==bl[b.l])return a.r<b.r;return a.l<b.l;}
inline void add(int x){now.set(x+cnt[x]);cnt[x]++;}//标记当前点的值出现 
inline void del(int x){cnt[x]--;now.reset(x+cnt[x]);}//标记当前点的值去除 
inline void solve()
{
	if(m==0)return ; //如果之前就已经全完成了就直接退出 
	int tot=0,i=1;
	now.reset();//清空now数组 
	for(i=1;i<=M-10;i++)//一次最多进行25000次查询操作 
	{
		if(m==0)break;//如果进行完了就退出 
		m--;
		ans[i]=0;//清空当前询问的答案 
		sum[i].set();//当前询问的集合全设为1 
		for(int j=1;j<=3;j++)//三个区间 
		{
			e[++tot].l=read();e[tot].r=read();//输入区间信息 
			e[tot].id=i;//标号 
			ans[i]+=e[tot].r-e[tot].l+1;//累加当前点的答案,假设一开始全不重合 
		}
	}
	sort(e+1,e+tot+1,cmp);//询问排序 
	int nl=1,nr=0,t=i-1;//t是当前询问的个数 
	for(int i=1;i<=tot;i++)
	{
		while(nl>e[i].l)add(a[--nl]);//正常莫队 
		while(nr<e[i].r)add(a[++nr]);
		while(nl<e[i].l)del(a[nl++]);
		while(nr>e[i].r)del(a[nr--]);
		sum[e[i].id]&=now;//当前序列内的元素的数量,三个区间都进行过&操作后sum里面存放的就是当前重合的数的离散化后的值 
	}
//	cout<<"T:"<<t<<endl;
	for(int i=1;i<=t;i++)
		cout<<ans[i]-(int)sum[i].count()*3<<endl;//减去重合的数的数量输出答案 
}
signed main()
{
	n=read();m=read();
	kc=sqrt(n);
	for(int i=1;i<=n;i++)
	  a[i]=read(),bl[i]=(i-1)/kc+1;
	int tmp[N];
	memcpy(tmp,a,sizeof(a));//复制数组 
	sort(tmp+1,tmp+n+1);//排序 
	for(int i=1;i<=n;i++)//离散化 
	  a[i]=lower_bound(tmp+1,tmp+n+1,a[i])-tmp;//直接在原数组上修改 
	for(int i=1;i<=4;i++)//分四次进行 
	{
		solve();
		for(int i=1;i<=n;i++)
		  cnt[i]=0; //每次都要清空cnt数组 
	}
	return 0;
}

标签:ch,int,sum,兔子,RE,while,Ynoi2016,now,P4688
From: https://www.cnblogs.com/Multitree/p/17278797.html

相关文章

  • 兔子问题,斐波那契数列(for循环,数组)
    古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少?程序分析:兔子的规律为......
  • C语言:生兔子
    //题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?//1.程序分析:兔子的规......
  • C语言死兔子问题
    问题:开局一对兔子三个月开始产子小兔子三个月后开始产子生一对兔子…兔子都不死计算有多少只兔子#include<stdio.h>intblamedRabbit(intm){intfb1,fb2;fb......
  • 兔子与兔子(hash模板题)
    题意描述:很久很久以前,森林里住着一群兔子。有一天,兔子们想要研究自己的DNA序列。我们首先选取一个好长好长的DNA序列(小兔子是外星生物,DNA序列可能包含26个小写英文字......
  • Solution Set -「DS 专题」兔年的兔子写 DS 会有小常数吗?
    目录Day1「Ynoi2009」「洛谷P6109」rprmq1^「YnoiEasyRound2021」「洛谷P8512」TEST_152「Ynoi2005」「洛谷P7907」rmscne「Ynoi2010」「洛谷P6105」y-fasttr......
  • python入门学习笔记002--趣学Python算法--第2例兔子产子
    例题如下:有一对兔子,从出生后的第3个月起每个月都生一对兔子。小兔子长到第3个月后每个月又生一对兔子,假设所有的兔子都不死,问30个月内每个月的兔子总对数为多少?  个......
  • 兔子牧尼 & 名为Amare的苹果 & 遇见 另一只兔子
    牧尼是一只兔子他安静敏感善良又迟钝慵懒记仇他的朋友们都说牧尼有一种忧郁的气质朋友们总能从他的眼睛里察觉到这一点牧尼不太喜欢向外界透露自己的心情......
  • 纯JavaScript入门级小游戏:兔子抢金币(附演示地址+源码)
    Hello,大家好,我是兔哥,我又来分享好玩的入门级项目啦。今天给大家带来的是一个纯JavaScript入门级小游戏:兔子抢金币,规则非常简单,控制屏幕上的兔子去接天上掉下来的金币,接满20......
  • 喵星之旅-狂奔的兔子-centos7安装MySQL 8
    这里的MySQL版本是8.0.30一、卸载原有的,包括默认有的和后来自己装的,参看喵星之旅-狂奔的兔子-centos7安装MySQL5.5二、下载文件,参考资料同上三、安装解压: tar-xvf......
  • 狼抓兔子
    狼抓兔子思路将面看成点,然后将边转换成面和面之间的边,然后跑一遍最短路,就可以了。但是具体那个是起点,那个是终点,是这么进行确定的,我还不太确定。代码#include<bits/s......