题解
乍看一眼题目,觉得可以前缀和可以解决
翻了众多大佬的题解,发现的确可以解决
原理·请移步这位大佬的题解
last[ i ]记录i最后一次出现的位置
根据大佬给出的样例 1 2 3 1
sum[1] - 1 0 0 0
sum[2] - 1 1 0 0
sum[3] - 1 1 1 0 统计[2,3]时就是前缀和:sum[3]-sum[2-1] = 1 1 1 0 - 1 1 0 0 = 0 1 1 0 = 2
sum[4] - 0 1 1 1 统计[2,4]时,这个时候[1,1]区间被更新了,sum[1] = 0 0 0 0,所以sum[4] - sum[2-1] = 0 1 1 1 - 0 0 0 0 = 3
可以看到更新之后就丢失了之前的信息了,所以需要离线处理 (主席树除外)
提交过程中发现代码正确但是T几个点那么零点几秒,多交几次就好,卡的过,
也许这就是没有读入挂的人的悲哀
主席树和分块T了QAQ。
离线线段树
emmm…提交的过程中发现有几个点会T那么零点几秒,多交几次就好了
#include <bits/stdc++.h>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int N=1e6+1;
int a[N];
int n,m,k;
struct Query{
int l,r,id;
void init(int x){
scanf("%d%d",&l,&r);
id=x;//问题编号 用于离线
}
}q[N];
bool cmp(Query a,Query b){//根据区间右端点排排列
if(a.r==b.r){
return a.l<b.l;
}
return a.r<b.r;
}
int last[N];//标记数字最后出现的位置
int sum[N<<2];
void pushup(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int pos,int val,int l,int r,int rt){
if(l==r){
sum[rt]+=val;
return;
}
int mid=l+r>>1;
if(pos<=mid) update(pos,val,lson);
else update(pos,val,rson);
pushup(rt);//子区间更新完后要更新整个区间
}
int query(int L,int R,int l,int r,int rt){
if(L<=l && R>=r){
return sum[rt];
}
int mid=l+r>>1,res=0;
if(L<=mid)res+=query(L,R,lson);
if(R>mid)res+=query(L,R,rson);
return res;
}
int ans[N];
int main(){
scanf("%d",&n);
for (int i = 1; i <= n; ++i) {
scanf("%d",&a[i]);
}
scanf("%d",&m);
for (int i = 1; i <= m; ++i) {
q[i].init(i);
}
sort(q+1,q+1+m,cmp);
int cur=1;//当前已经更新到了哪个点
for (int i = 1; i <= m; ++i) {
for (int j = cur; j <= q[i].r; ++j) {
if(last[a[j]]){//如果更新过 要先消除之前的标记
update(last[a[j]],-1,1,n,1);
}
update(j,1,1,n,1);
last[a[j]]=j;
}
cur=q[i].r+1;
int left = q[i].l==1?0:query(1,q[i].l-1,1,n,1);
ans[q[i].id]=query(1,q[i].r,1,n,1)-left;//前缀和
}
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
return 0;
}
离线树状数组
第一次用树状数组,嗯,比线段树的快了一倍
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N];
int n,m,k;
struct Query{
int l,r,id;
void init(int i){
scanf("%d%d",&l,&r);
id=i;
}
}q[N];
bool cmp(Query a,Query b){
if(a.r==b.r){
return a.l<b.l;
}
return a.r<b.r;//重要的是右端点
}
int last[N],ans[N];
/*-----树状数组------*/
int sum[N];
int lowbit(int x){
return x & (-x);//返回 2^(x末尾0的个数)
}
void add(int pos,int val){
for (int i = pos; i <= n; i+=lowbit(i)) {//pos~n
sum[i]+=val;
}
}
int getSum(int pos){
int res=0;
for (int i = pos; i; i-=lowbit(i)) { //1~pos
res+=sum[i];
}
return res;
}
/*-----end------*/
int main(){
scanf("%d",&n);
for (int i = 1 ; i <= n; ++i) {
scanf("%d",&a[i]);//种类
}
scanf("%d",&m);
for (int i = 1; i <= m; ++i) {
q[i].init(i);
}
sort(q+1,q+1+m,cmp);
int cur=1;
for (int i = 1; i <= m; ++i) {
for (int j = cur; j <= q[i].r; ++j) {
if(last[a[j]]){
add(last[a[j]],-1);
}
add(j,1);
last[a[j]]=j;
}
cur=q[i].r+1;
ans[q[i].id]=getSum(q[i].r)-getSum(q[i].l-1);//前缀和
}
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
return 0;
}
主席树
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,k;
int last[N],a[N];
/*------------主席树----------------*/
int L[N*30],R[N*30],rt[N*30],sum[N*30];//tree
int tot;
void update(int pre,int& cur,int val,int pos,int l,int r){//原先的版本 现在的版本 传入的值 需插入的位置 左端点l 右端点r
cur=++tot;
L[cur]=L[pre];
R[cur]=R[pre];
sum[cur]=sum[pre]+val;
if(l==r) return ;
int mid=l+r>>1;
if(pos<=mid) update(L[pre],L[cur],val,pos,l,mid);
else update(R[pre],R[cur],val,pos,mid+1,r);
}
int query(int cur,int x,int y,int l,int r){
if(x<=l && r<=y){
return sum[cur];
}
int mid=l+r>>1;
int res=0;
if(x<=mid)res+=query(L[cur],x,y,l,mid);
if(y>mid)res+=query(R[cur],x,y,mid+1,r);
return res;
}
/*------------end----------------*/
int main(){
scanf("%d",&n);
int tmp=0;
for (int i = 1; i <= n; ++i) {
scanf("%d",&a[i]);
if(last[a[i]]){
update(rt[i-1],tmp,-1,last[a[i]],1,n);//emm tmp这个操作没看懂 以后再来琢磨琢磨
update(tmp,rt[i],1,i,1,n);
}else{
update(rt[i-1],rt[i],1,i,1,n);
}
last[a[i]]=i;
}
scanf("%d",&m);
for (int i = 1,l,r; i <= m; ++i) {
scanf("%d%d",&l,&r);
printf("%d\n", query(rt[r],l,r,1,n));
}
return 0;
}
分块
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+1;
int a[N];
int n,m,k;
struct Query{
int l,r,id;
void init(int x){
scanf("%d%d",&l,&r);
id=x;
}
}q[N];
int block;
bool cmp(Query a,Query b){
if(a.r/block==b.r/block)return a.l/block<b.l/block;
return a.r/block<b.r/block;//节省时间的关键点
}
int ans[N];
int sum=0;
int cnt[N];//统计区间内每一种数字的个数
void del(int x){
if(cnt[x]==1)sum--;
cnt[x]--;
}
void add(int x){
if(!cnt[x]) sum++;
cnt[x]++;
}
int main(){
scanf("%d",&n);
for (int i = 1; i <= n; ++i) {
scanf("%d",&a[i]);
}
scanf("%d",&m);
block=n/sqrt(m);//划分
for (int i = 1; i <= m; ++i) {
q[i].init(i);
}
sort(q+1,q+1+m,cmp);
int l=0,r=0;
for (int i = 1; i <= m; ++i) {
while(l<q[i].l)del(a[l++]);//区间外删除
while(l>q[i].l)add(a[--l]);//区间内增加
while(r>q[i].r)del(a[r--]);
while(r<q[i].r)add(a[++r]);
ans[q[i].id]=sum;
}
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
return 0;
}