4939: [Ynoi2016]掉进兔子洞

题意

题面很长,取重要部分就好了
一个长为 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。

题解

这题看了路牌——莫队,什么想法都没有
三个区间莫什么队啊。。
然后我们可以稍作思考,其实答案就是三个区间的个数和-公共部分嘛。。
于是就是要就公共部分
看了看题解,才知道怎么做。。感觉并不难
公共部分其实我们是可以用bitset来维护的
对于每一个数字,一开始的下标为x
第一次的时候就标记x出现了,第二次标记为x+1出现了。。如此类推
就可以解决一个数字出现多次的情况了。。
然后答案就是三个bitset与一下,看一下1的个数就可以了
然后呢,听说这题卡常数(其实我觉得这复杂度本来就有点大,但是给了80s的时限还可以吧),于是要手写bieset
这里大概说一下手写的bitset怎么写:
bitset其实就是bool数组,但是这样太大/慢了
我们考虑将bool数组开为int
然后就可以位压了
每32位压一压。。
这一块一起处理,这样时空复杂度都是1/32了

CODE:(时空垫底)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
typedef unsigned int Int;
const int N=100005 ;
const int M=33334;//这么多操作做一次 
int n,m,nn;
int cal[(1<<16)+10];//这个数有多少个1 
int belong[N];
struct qa{int x,id;}a[N];
int A[N];//离散化后的数组 
int b[N];
int head[N];//离散化后这个值在哪里 
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
bool cmp (qa x,qa y){return x.x<y.x;}
int l1[N],r1[N],l2[N],r2[N],l3[N],r3[N];
int ans[N];
void Init ()
{
    n=read();m=read();nn=sqrt(n);
    for (int u=0;u<(1<<16);u++) cal[u]=cal[u>>1]+(u&1);
    for (int u=1;u<=n;u++) belong[u]=(u-1)/nn+1;
    for (int u=1;u<=n;u++)  {a[u].x=read();a[u].id=u;}
    sort(a+1,a+1+n,cmp);
    int tot=1;A[a[1].id]=tot;
    for (int u=2;u<=n;u++)
    {
        if (a[u].x!=a[u-1].x) tot++;
        A[a[u].id]=tot;
    }
    for (int u=1;u<=n;u++) b[u]=A[u];
    sort(b+1,b+1+n);
    for (int u=1;u<=n;u++)
        if (b[u]!=b[u-1])
            head[b[u]]=u;
    for (int u=1;u<=m;u++)
    {
        l1[u]=read();r1[u]=read();
        l2[u]=read();r2[u]=read();
        l3[u]=read();r3[u]=read();
        ans[u]=(r1[u]-l1[u]+1)+(r2[u]-l2[u]+1)+(r3[u]-l3[u]+1);
    }
}
struct qq
{
    int l,r,id;
}q[(M+10)*3];int len;
bool cmp1 (qq x,qq y)
{
    return belong[x.l]==belong[y.l]?x.r<y.r:belong[x.l]<belong[y.l];
}
struct bs //手写bitset 
{
    Int S[3125];
    inline void vis (int x)//如果之前存在就把它加上,否则减去 
    {S[x>>5]^=(1U<<(x&31));}
    int count ()//有多少个1
    {
        int tot=0;
        for (int u=0;u<=3124;u++)
            tot=tot+cal[S[u]>>16]+cal[S[u]&65535];
        return tot;
    }
    void clear (){memset(S,0,sizeof(S));}
}now,c[M+10];
bs operator ^ (bs x,bs y)
{
    bs z;
    for (int u=0;u<=3124;u++)   z.S[u]=(x.S[u]^y.S[u]);
    return z;
}
bs operator & (bs x,bs y)
{
    bs z;
    for (int u=0;u<=3124;u++)   z.S[u]=(x.S[u]&y.S[u]);
    return z;
}
int h[N];//每个位置使用到的下标 
void Del (int x){now.vis(head[x]+h[x]-2);h[x]--;}
void Add (int x){h[x]++;now.vis(head[x]+h[x]-2);}
void solve1 ()
{
    now.clear();memset(h,0,sizeof(h));
    memset(c,255,sizeof(c));//全部清为1
    int l=1,r=0;
    sort(q+1,q+1+len,cmp1); 
    /*for (int u=1;u<=len;u++)
        printf("YES:%d %d %d\n",q[u].l,q[u].r,q[u].id);*/

    for (int u=1;u<=len;u++)
    {   
        while (l>q[u].l) Add(A[--l]);
        while (l<q[u].l) Del(A[l++]);
        while (r>q[u].r) Del(A[r--]);
        while (r<q[u].r) Add(A[++r]);
        c[q[u].id]=c[q[u].id]&now;
        //printf("%d %d\n",q[u].id,c[q[u].id].count());
    }
}
void solve ()
{
    for (int u=1;u<=m;u+=M)
    {
        len=0;
        for (int i=u;i<=u+M-1;i++)
        {
            if (i>m) break;
            q[++len]=qq{l1[i],r1[i],i-u+1};
            q[++len]=qq{l2[i],r2[i],i-u+1};
            q[++len]=qq{l3[i],r3[i],i-u+1};
        }
        solve1();
        for (int i=u;i<=u+M-1;i++)
        {
            if (i>m) break;
            printf("%d\n",ans[i]-3*c[i-u+1].count());
        }
    }
}
int main()
{
    Init();
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值