bzoj 4939: [Ynoi2016]掉进兔子洞

Posted zjxxcn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 
View Code

 

 

 

以上是关于bzoj 4939: [Ynoi2016]掉进兔子洞的主要内容,如果未能解决你的问题,请参考以下文章

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

bzoj 4939: [Ynoi2016]掉进兔子洞

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

p4688 [Ynoi2016]掉进兔子洞

luogu P4688 [Ynoi2016]掉进兔子洞

luogu P4688 [Ynoi2016]掉进兔子洞