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 。
当 l−1 或者 r+1 即往队列中加入一个数时,直接判断这个数的 v[x] 是否为0,如果是就加入。
当 l+1 或者 r−1 即从队列中删除一个数时,要判断在之后的删除中是否还会出现同样的数,如果还会出现的话这一次先不管他;否则判断下一次出现是否在所求区间中,如果不在就删除。
#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