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');
}

(```)

posted @ 2020-03-11 16:41  starseven  阅读(128)  评论(0编辑  收藏  举报