LuoguP1972书架
其实我对树状数组及一系列树据结构掌握一直如同菜鸟
打了这么久连别人认为是模板的题都想不出来
所以我采用题海战术,妄想有些突破
本题的意思是选取随机一段l~r的区间,询问这个区间里有多少种不同贝壳。
本来看到区间和统计两个词语就应该想到树状数组&线段树
但是仔细一想,如果是线段树的合并,必须满足相加的性质(例如数量,长度等)或者一些可以“拼接的东西”(例如hash值,相同颜色小球的数量等……)这些便可以用线段树的合并。
而现在是统计种类的多少,显然不能用普通的合并(1,3,5)&(1,4,5) 你总不能说合并后种类为3+3==6种吧(显然是4种)
重点来了
重点来了
终点来了
那我们怎么统计种类的多少呢?
仔细分析一下题目性质,要求我们求l~r之间种类数量,如果我们普通的模板解决不了怎么办?
我们假设我们求的是1~l,我们会怎么求
这个时候思路应该就会比较明显,但是还是没有到想出来的地步(所以没想出来的朋友们不要慌)
我们直接for循环过去,用一个vis数组保存,如果碰到一个数x,!vis[x],那么我们的ans++;(ans代表的是颜色种类)
如果vis[x]>0 ,那我们就直接continue; 对不对?
那么代码如下:
(```)
for(register int i=l;i<=r;i++){
if(!vis[color[i]]) vis[color[i]]=true,ans++;
//color[i]代表的是i号数字的种类
}
(```)
这个时候时间复杂度就是n*m(长度乘以询问数)
想不到更好的算法怎么办?
那就优化噻!
我们依据我们算法的思想——本质上是统计出现的种类,重复的舍去,而我们要求的lr又自然地想到1l 和 1~r两个区间,那么我们冥思苦想,怎么才能用区间表示呢
我们将集合S代表1~l-1 & lr两个区间公有的元素,那么我们可以只选择1l-1的元素++,或者只选择lr的元素++;我们要统计lr,那么我们理所应当加在l~r上:
将其通俗一点,便是在循环遍历时,if(vis[color[i]]) 上一个color[i]的位置--;i++;
这个时候便可以用树状数组统计。
而区间的话我们可以离线处理,因为1l这个初始点并不能包含这个lr,所以说我们按照r的大小排序,这样上一个color[i]--时,就不会影响答案
所以完整的代码如下
(```)
#include<cstdio>
#include<iostream>
#include<algorithm>
#define Starseven main
#define ri register int
using namespace std;
const int N=1e6+20;
int n,m,cnt[N],before[N],tr[N];
struct node{
int kind,loc;
}num[N];
struct noe{
int ans,l,r,id;
}area[N];
bool cmp(const node &a,const node &b){
if(a.kind!=b.kind) return a.kind<b.kind;
else return a.loc<b.loc;
}
bool fuck(const noe &a,const noe &b){
return a.r<b.r;
}
void Insert(int x,int va){
for(ri i=x;i<=n;i+=i&-i) tr[i]+=va;
}
int Getsum(int x){
int re=0;
for(ri i=x;i;i-=i&-i) re+=tr[i];
return re;
}
bool again(const noe &a,const noe &b){
return a.id<b.id;
}
int read();
void write(int);
void wrote(int);
int Starseven(){
n=read();
for(ri i=1;i<=n;i++){
num[i].loc=i;cnt[i]=num[i].kind=read();
}
sort(num+1,num+1+n,cmp);
for(ri i=1;i<=n;i++){
before[num[i].loc]=num[i].kind==num[i-1].kind?num[i-1].loc:0;
}
m=read();
for(ri i=1;i<=m;i++){
area[i].l=read(),area[i].r=read();
area[i].id=i;
}
sort(area+1,area+1+m,fuck);
int tail=1;
for(ri i=1;i<=n;i++){
Insert(i,1);
if(before[i]) Insert(before[i],-1);
while(area[tail].r==i){
area[tail].ans=Getsum(i)-Getsum(area[tail].l-1);
tail++;
}
}
sort(area+1,area+1+m,again);
for(ri i=1;i<=m;i++) write(area[i].ans);
return 0;
}
int read(){
char ch=getchar();int re=0,op=1;
while(ch<'0'||ch>'9'){
if(ch=='-')op=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
re=(re<<3)+(re<<1)+ch-'0';
ch=getchar();
}
return re*op;
}
inline void write(int x){
wrote(x);
puts("");
}
inline void wrote(int x)
{
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
wrote(x/10);
putchar(x%10+'0');
}
(```)