【BZOJ 3236】 [Ahoi2013]作业

3236: [Ahoi2013]作业

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

Input

【BZOJ 3236】 [Ahoi2013]作业_第1张图片

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 100005
using 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

你可能感兴趣的:(树状数组,OI,乱搞,bzoj,莫队)