【BZOJ 3236】 [Ahoi2013]作业

来源:互联网 发布:服装设计专用软件 编辑:程序博客网 时间:2024/05/29 10:25

3236: [Ahoi2013]作业

Time Limit: 100 Sec Memory Limit: 512 MB
Submit: 819 Solved: 307
[Submit][Status][Discuss]
Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

3 4

1 2 2

1 2 1 3

1 2 1 1

1 3 1 3

2 3 2 3

Sample Output

2 2

1 1

3 2

2 1
HINT

N=100000,M=1000000

Source

By wangyisong1996加强数据

莫队算法+树状数组。

离线来做,将询问按照分好的块来排序。

接下来就是考虑[l,r]如何转移到[l,r±1][l±1,r]

①对于第一问,直接建权值线段树加加减减即可

②第二问有点麻烦,我的做法是:
树状数组维护一个数值在这段区间出现过为1,否则为0的前缀和;

记录下pre[i],ne[i]表示与i数值的相同的前一个,后一个在哪;

v[x]表示x这种数值是否在树状数组中值是否为1

l1或者r+1即往队列中加入一个数时,直接判断这个数的v[x]是否为0,如果是就加入。

l+1或者r1即从队列中删除一个数时,要判断在之后的删除中是否还会出现同样的数,如果还会出现的话这一次先不管他;否则判断下一次出现是否在所求区间中,如果不在就删除。

#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#define M 100005using namespace std;int pos[M],a[M],v[M],pre[M],ne[M],la[M],t[M][2],ans1[M*10],ans2[M*10],n,m;struct Query{    int l,r,a,b,id;}q[M*10];int lowbit(int x){    return x&(-x);}bool cmp(Query a,Query b){    if (pos[a.l]==pos[b.l]) return a.r<b.r;    return pos[a.l]<pos[b.l];}void Update1(int x,int k){    for (int i=x;i<=M-5;i+=lowbit(i))        t[i][0]+=k;}void Update2(int s,int x,int k,int l,int r){    if (k==1)    {        if (v[x]) return;        for (int i=x;i<=M-5;i+=lowbit(i))            t[i][1]++;        v[x]=1;        return;    }    if (!v[x]) return;    if (k==-1&&s<l&&s) return;    if (k==-2&&s>r) return;    if (s>r||s<l)    {        for (int i=x;i<=M-5;i+=lowbit(i))            t[i][1]--;        v[x]=0;    }}int Query(int x,int k){    int ans=0;    for (int i=x;i;i-=lowbit(i))        ans+=t[i][k];    return ans;}void read(int &tmp){    tmp=0;    char ch=getchar();    for (;ch<'0'||ch>'9';ch=getchar());    for (;ch>='0'&&ch<='9';ch=getchar())        tmp=tmp*10+ch-'0';}int main(){    read(n),read(m);    int block=sqrt(n);    for (int i=1;i<=n;i++)    {        pos[i]=(i+block-1)/block;        read(a[i]),pre[i]=la[a[i]],la[a[i]]=i;    }    for (int i=1;i<=n;i++)        la[i]=0;    for (int i=n;i;i--)        ne[i]=la[a[i]],la[a[i]]=i;    for (int i=1;i<=m;i++)        read(q[i].l),read(q[i].r),read(q[i].a),read(q[i].b),q[i].id=i;    sort(q+1,q+1+m,cmp);    int l=q[1].l,r=q[1].l;    Update1(a[l],1),Update2(0,a[l],1,0,0);    for (int i=1;i<=m;i++)    {        int nl=q[i].l,nr=q[i].r;        if (nr>r)        {            r++;            for (;r<=nr;r++)                Update1(a[r],1),Update2(0,a[r],1,0,0);            r--;        }        if (nl>l)        {            for (;l<nl;l++)                Update1(a[l],-1),Update2(ne[l],a[l],-1,nl,nr);        }        if (nl<l)        {            l--;            for (;l>=nl;l--)                Update1(a[l],1),Update2(0,a[l],1,nl,nr);            l++;        }        if (nr<r)        {            for (;r>nr;r--)                Update1(a[r],-1),Update2(pre[r],a[r],-2,nl,nr);        }        ans1[q[i].id]=Query(q[i].b,0)-Query(q[i].a-1,0);        ans2[q[i].id]=Query(q[i].b,1)-Query(q[i].a-1,1);    }    for (int i=1;i<=m;i++)        printf("%d %d\n",ans1[i],ans2[i]);    return 0;}

这里写图片描述

感悟:
1.莫队好慢啊,树套树会快一点。。

2.这题细节有点多:
①首先要注意主程序中更新l,r的顺序,避免出现l>r的情况
②在处理第二问的Update2中,如果是删除要注意v[x]是否为1

1 0