BZOJ 4939 (Ynoi 2016)掉进兔子洞(莫队+压位)

4939: [Ynoi2016]掉进兔子洞

Description

您正在打galgame,然后突然发现您今天太颓了,于是想写个数据结构题练练手:
一个长为 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。

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


显然是要求 3i=1(rili+1)Maxi=1min(cnt1[i],cnt2[i],cnt3[i]) ∑ i = 1 3 ( r i − l i + 1 ) − ∑ i = 1 M a x m i n ( c n t 1 [ i ] , c n t 2 [ i ] , c n t 3 [ i ] ) cntj[i]ij c n t j [ i ] 表 示 数 字 i 在 区 间 j 中 出 现 次 数

考虑求后面的部分。首先肯定要离散化。
可以将每个询问拆成三个,分别处理出每个区间的 cnt c n t 数组。而 cnt c n t 数组可以用莫队来得到。

现在考虑如何较快的将三个 cnt c n t 取最小后求和。这个可以用 bitset b i t s e t 来做到。
考虑离散化的时候,比如样例1 2 2 3 3,离散化后得到1 2 2 4 4,那么在 bitset b i t s e t 中,就可以用第3个bit位来表示第二个出现的2,用第2个bit位来表示第一个出现的2。然后只需要把三个bitset与起来就得到答案了。


代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<bitset>
#include<cmath>
#define N 100005
using namespace std;
const int T=25000;
bitset<100000>F[25001],f;
int n,A[N],B[N],cnt[N],id[N],S,tot,ans[N],Ans[N],TOT;
bool mark[N];
struct node{int l,r,id;}K[N];
bool cmp(node a,node b)
{
    if(id[a.l]==id[b.l])return a.r<b.r;
    return id[a.l]<id[b.l];
}
void UD(int k,int ty)
{
    k=A[k];cnt[k]+=ty;
    if(ty==1)f[k+cnt[k]-2]=1;
    else f[k+cnt[k]-1]=0;
}
void Solve(int m)
{
    int i,j,k,L,R,l1,l2,l3,r1,r2,r3;
    memset(cnt,0,sizeof(cnt));
    memset(mark,0,sizeof(mark));
    f.reset();tot=0;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d%d%d%d",&l1,&r1,&l2,&r2,&l3,&r3);
        K[++tot]=(node){l1,r1,i};
        K[++tot]=(node){l2,r2,i};
        K[++tot]=(node){l3,r3,i};
        ans[i]=r3+r2+r1-l3-l2-l1+3;
    }
    sort(K+1,K+tot+1,cmp);
    L=1;R=0;
    for(i=1;i<=tot;i++)
    {
        while(R<K[i].r)UD(++R,1);
        while(R>K[i].r)UD(R--,-1);
        while(L<K[i].l)UD(L++,-1);
        while(L>K[i].l)UD(--L,1);
        if(mark[K[i].id])F[K[i].id]&=f;
        else F[K[i].id]=f,mark[K[i].id]=1;
    }
    for(i=1;i<=m;i++)
    {
        k=F[i].count();
        printf("%d\n",ans[i]-3*k);
    }
}
int main()
{
    int i,j,k,m,x,y,l,r;
    scanf("%d%d",&n,&m);S=sqrt(n);
    for(i=j=1;i<=n;i++)scanf("%d",&A[i]),B[i]=A[i],id[i]=i%S?j:j++;
    sort(B+1,B+n+1);
    for(i=1;i<=n;i++)A[i]=lower_bound(B+1,B+n+1,A[i])-B;
    while(m)
    {
        if(m<=T)Solve(m),m=0;
        else Solve(T),m-=T;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值