采花题解
By李宇航
树状数组
首先我们要有离线处理的思想,即把左右端点记录下来,并且记录下来每个问题的pos。然后我们要对右端点升序排序。当然,还有更加重要的预处理操作。
我们要记录每一个位置该种颜色花的上一次出现的位置,用两个数组Front(下标为花的颜色),Prev(下标为位置)即可实现,如果没有出现,则Prev=0;
然后我们就从1-N开始扫了,每一次先是ADD(i)。对于每个i,如果prev[i]=0
那我们没有必要来更新了。我们只对prev[i]存在的更新,令r= prev[i],l=prev[r],那么我们就找到了一个更新的区间,我们将l向下更新-1,将r向下更新+1,于是就完成了去重操作,然后对以该点为右端点的ASK 从左端点GETSUM就可以了
有图有真相 (自己画的,凑合看吧)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100010*10;
int Front[maxn],Prev[maxn],c[maxn],data[maxn],Ans[maxn];
int N,C,M;
int lowbit(int x)
{
return x&(-x);
}
struct node
{
int pos,l,r;
}A[maxn];
bool comp(node a,node b)
{
return a.r<b.r||a.r==b.r&&a.l<b.r;
}
void updata(int x,int data)
{
for(;x>0;x-=lowbit(x))
{
c[x]+=data;
}
}
int get(int x)
{
int ans=0;
for(;x<=N;x+=lowbit(x))
{
ans+=c[x];
}
return ans;
}
void ADD(int x)
{
if(!Prev[x])return;
int r=Prev[x],l=Prev[r];
updata(r,1);
if(l)updata(l,-1);
}
int main()
{
scanf("%d%d%d",&N,&C,&M);
for(int i=1;i<=N;i++)
{
scanf("%d",&data[i]);
Prev[i]=Front[data[i]];
Front[data[i]]=i;
}
for(int i=1;i<=M;i++)
{
scanf("%d%d",&A[i].l,&A[i].r);
A[i].pos=i;
}
sort(A+1,A+M+1,comp);
int ha=1;
for(int i=1;i<=N;i++)
{
ADD(i);
while(ha<=M&&A[ha].r==i)
{
Ans[A[ha].pos]=get(A[ha].l);
ha++;
}
}
for(int i=1;i<=M;i++)printf("%d\n",Ans[i]);
return 0;
}
图上解释的是对第三朵颜色相同的花的操作
对于第二颜色相同的花,我们只把他的前驱加向下更新+1即可(所以略去)
所以我们得到对于每朵花 在其前驱(右端点) 和前驱的前驱(左端点)存在的情况下,分别对其进行更新。
题解如有错误,欢迎指正!