bzoj 4939: [Ynoi2016]掉进兔子洞

有 m 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立。

注意这里删掉指的是一个一个删,不是把等于这个值的数直接删完,

比如三个区间是 [1,2,2,3,3,3,3] ,  [1,2,2,3,3,3,3] 与  [1,1,2,3,3],就一起扔掉了 1 个 1,1 个 2,2 个 3。

Input

第一行两个数表示 n , m。

第二行 n个数表示 a[i]。

之后 m 行,每行 6 个数 l1 , r1 , l2, r2 , l3 , r3 表示这三个区间。

Output

对于每个询问,输出一个数表示答案。

Sample Input

5 2
1 2 2 3 3
1 2 2 3 3 4
1 5 1 5 1 5

Sample Output

3
0

HINT

n , m <= 100000 , 1 <= a[i] <= 1000000000

首先,很显然这题是要用莫队来处理的。我们先把输入的数字另外排一下序,然后记录一下pi表示每一个数字对应在排好序的数列里面是排第几个。询问的时候要把一个询问拆成3个询问,然后再并起来。然后在莫队的时候记录cnti表示当前数字i出现的次数,再开一个bitset,假设当前需要加入bitset的数字是x,我们就令bitset中的第px+cntx位为1,然后++cntx。这样就会使得把三个bitset并起来之后,恰好剩下的就是在三个区间都出现了的数字,也就是需要删除的数字,我们就只需要看并起来之后还剩多少个1就行了

还有一个问题,就是我们必须要对每一个询问(拆之前)都要开一个bitset来存答案,但这会开不下。所以我们就要平衡一下空间复杂度和时间复杂度,把询问分为几块来处理

所以其实程序还可以更快一些,那就是在给所有的块排好序之后再分块。但这样写起来有点麻烦,所以我就懒得卡了(很有道理,但是我没写)

ps: 这里我发现了bitset还是调用他的函数比较快。

比如now[x]=1 我改成了now.flip(x)  就快了很多。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int const N=100000+10;
 4 int const B=350;
 5 int const T=25000;
 6 struct query{
 7     int l,r,id;
 8     bool operator < (const query &rhs)const{
 9         if(l/B!=rhs.l/B) return  l/B<rhs.l/B;
10         return r<rhs.r;
11     }
12 }q[N];
13 int a[N],b[N],n,m,sum[N],ans[N],tot,cnt[N],p[N];
14 bitset<N> s[T+1],now;
15 void solve(){
16     for(int i=1;i<=tot/3;i++) s[i].set();
17     for(int i=1;i<=100000;i++) cnt[i]=0;
18     now.reset();
19     int l=1,r=0;
20     for(int i=1;i<=tot;i++){
21         while (l>q[i].l){
22             --l; now.flip(p[a[l]]+cnt[a[l]]);  cnt[a[l]]++;
23         }
24         while (r<q[i].r){
25             ++r; now.flip(p[a[r]]+cnt[a[r]]); cnt[a[r]]++;
26         }
27         while (l<q[i].l){
28             cnt[a[l]]--; now.flip(p[a[l]]+cnt[a[l]]);l++;
29         }
30         while (r>q[i].r){
31             cnt[a[r]]--; now.flip(p[a[r]]+cnt[a[r]]);r--;
32         }
33         s[q[i].id]&=now;
34     }
35     for(int i=1;i<=tot/3;i++)   {
36         printf("%d\n",sum[i]-s[i].count()*3);
37     }
38 }
39 int main(){
40     scanf("%d%d",&n,&m);
41     for(int i=1;i<=n;i++)
42         scanf("%d",&a[i]),b[i]=a[i];
43     sort(b+1,b+n+1);
44     int k=unique(b+1,b+n+1)-b-1;
45     for(int i=1;i<=n;i++)
46         a[i]=lower_bound(b+1,b+k+1,a[i])-b;
47     memcpy(b,a,sizeof(a));
48     sort(b+1,b+n+1);
49     for(int i=n;i>=1;i--)
50         p[b[i]]=i;
51     int num=(m+T-1)/T;
52     for(int i=1;i<=num;i++){
53         tot=0;
54         for(int j=(i-1)*T+1;j<=min(i*T,m);j++){
55             int x1,y1,x2,y2,x3,y3;
56             scanf("%d%d%d%d%d%d",&x1,&y1,&x2,&y2,&x3,&y3);
57             q[++tot].l=x1; q[tot].r=y1; q[tot].id=j-(i-1)*T;
58             q[++tot].l=x2; q[tot].r=y2; q[tot].id=j-(i-1)*T;
59             q[++tot].l=x3; q[tot].r=y3; q[tot].id=j-(i-1)*T;
60             sum[j-(i-1)*T]=y1-x1+y2-x2+y3-x3+3;
61         }
62         sort(q+1,q+tot+1);
63         solve();
64     }
65     return 0;
66 }

原文地址:https://www.cnblogs.com/ZJXXCN/p/11421009.html

时间: 2024-11-12 19:30:30

bzoj 4939: [Ynoi2016]掉进兔子洞的相关文章

BZOJ 4939 [Ynoi2016]掉进兔子洞(莫队+bitset)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4939 [题目大意] 给出一个数列,每个询问给出三个区间,问除去三个区间共有的数字外, 还剩下几个数字,注意删去的是共有的数字个数,不是数字种类,统计时候也一样 [题解] 首先,答案为区间长度和减去区间并数字个数和的三倍. 所以题目转化为求区间并.很显然在开始对数据可以进行离散化. 考虑每个数字只出现一次的情况,我们可以用bitset来统计区间某个数字是否存在, 莫队处理查询每个区间,

luogu P4688 [Ynoi2016]掉进兔子洞 bitset 莫队

题目链接 luogu P4688 [Ynoi2016]掉进兔子洞 题解 莫队维护bitset区间交个数 代码 // luogu-judger-enable-o2 #include<cmath> #include<bitset> #include<cstdio> #include<cstring> #include<algorithm> inline int read() { int x = 0,f = 1; char c = getchar();

bzoj4939: [Ynoi2016]掉进兔子洞

将权值排序,设权值x排序后在[l,r]出现,x在区间中出现k次,则用[l,l+k-1]为1,[l+k,r]为0来表示x的出现次数 用bitset表示可重集中每个元素的出现次数,用莫队处理出询问区间对应的bitset,通过取and后求1的个数得到答案 #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> typedef unsigned int u32; typedef u

p4688 [Ynoi2016]掉进兔子洞

传送门 分析 我们考虑先将所有数离散化 之后我们对于每个状态用一个bitset来记录 其中第i段表示颜色i的信息 对于每一段信息均是段首若干1,剩余若干0表示这种颜色有多少个 于是我们不难想到莫队 答案就是1的总个数-异或值的1的个数乘3 但是我们发现开1e5*1e5的bitset会炸 于是我们考虑把1e5个询问分组,一次只考虑25000个询问 这个样子就能过了 原文地址:https://www.cnblogs.com/yzxverygood/p/10527348.html

luogu P4688 [Ynoi2016]掉进兔子洞

luogu 我们要求的答案应该是三个区间长度\(-3*\)在三个区间中都出现过的数个数 先考虑数列中没有相同的数怎么做,那就是对三个区间求交,然后交集大小就是要求的那个个数.现在有相同的数,考虑给区间中的数安排位置,即区间中如果出现了多个相同的数\(x\),那么就把第一个\(x\)放在\(x\)这种数要放的第一个位置,把第二个\(x\)放在第二个对应位置,依次类推.具体的,我们用桶维护区间内所有数的出现次数,然后给每种数安排一个初始下标\(ps_x\),使得后面过程中放数不会重叠,如果数\(x\

[bzoj 4939][Ynoi 2016]掉进兔子洞

传送门 Description 一个长为 n 的序列 a. 有 m 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立. 注意这里删掉指的是一个一个删,不是把等于这个值的数直接删完, 比如三个区间是 [1,2,2,3,3,3,3] , [1,2,2,3,3,3,3] 与 [1,1,2,3,3],就一起扔掉了 1 个 1,1 个 2,2 个 3. Solution 弱化版是luoguP3674,这里放上它的题解 戳我 要找相同的元素就是集合求交

变量不加 var 声明——掉进坑中,无法自拔!

整整一下午,都在解决 window.onresize 中方法丢失不执行的问题!姿势固定在电脑前,颈椎病都犯了. 前些日子与大家分享了一下关于 防止jquery $(window).resize()多次执行其中方法的文章,没写全,留了一大堆问题,我理解的方法是这样的: function foo() { var resizable = null; window.onresize = function() { if (resizable) { clearTimeout(resizable) } res

C++ string中的几个小陷阱,你掉进过吗?

C++开发的项目难免会用到STL的string,使用管理都比char数组(指针)方便的多,但在得心应手的使用过程中也要警惕几个小陷阱,避免我们项目出bug却迟迟找不到原因. 1.  结构体中的string赋值问题直接通过一个例子说明,下面的例子会输出什么: #include <iostream> #include <string> #include <stdlib.h> using namespace std; struct flowRecord { string ap

人造卫星为什么会绕着地球转而不是停在太空中或者越飞越远.掉进地球的卫星为什么烧不完.

人造卫星为什么会绕着地球转而不是停在太空中或者越飞越远.掉进地球的卫星为什么烧不完.卫星被火箭推到太空中之后失去火箭的推动不就停在太空中或者因为惯性越飞越远了吗,为什么会绕着地球在椭圆形的轨道上飞?报废的卫星为什么又会掉下来?掉下来的卫星有的为什么会在大气层中没烧尽,那些卫星不是都不能耐高温的吗,应该一进大气层就熔了啊. 答: 第一点:卫星绕着地球轨道运行,不会飞出去也不会掉下来,归根到底都是万有引力的作用.可以这样类比,我们拿着绳子一头绑着石头用力做绕圈运动,绳子的拉力提供石头运行的向心力,石