[SP3267] DQUERY - D-query计算区间内颜色种数的离线做法——树状数组or线段树

English VietnameseGiven a sequence of n numbers a 1 {1} 1​ , a 2 {2} 2​ , …, a n {n} n​ and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence a i {i} i​ , a i+1 {i+1} i+1​ , …, a j {j} j​ .
题意:给定一串序列,然后m个询问,每个询问求该区间内颜色种类。
算法:离线处理,按照右端点排序,然后从左往右扫一遍
这里写图片描述
如图所示,我们指针每扫到一个地点就可以吧这种颜色在线段树或者树状数组上的区间修改继续到指针所示的位置,正如蓝色那样,红色和绿色因为之后还没有扫到新的红色和绿色所以没有更新修改,如果有一段区间它的右端点在指针所指的位置,它的左端点如果在S1就会算成3种颜色,因为三种颜色都更新到过了S1,而S2和S3却只有两种和一种,就这样不断地往后推,如果又有新的红色出现的话,红色的标记就会一直更新到红色出现的最后的位置。
下面附上用线段树的代码

/*================================
Author:ylsoi
Problem:SP3267
Time:2018.2.24
================================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
void File(){
#ifndef ONLINE_JUDGE
    freopen("spoj3267.in","r",stdin);
    freopen("spoj3267.out","w",stdout);
#endif
}
template<typename T>
void read(T &x){
    T _=0;char __=getchar();
    while(!isdigit(__))__=getchar();
    while(isdigit(__))_=(_<<1)+(_<<3)+(__^'0'),__=getchar();
    x=_;
}
template<typename T>
void write(T x){
    if(x==0){puts("0\n");return;}
    T _=1,__=0;
    while(_<=x)_*=10,++__;
    while(__--){
        _/=10;
        putchar(x/_+48);
        x%=_;
    }
    putchar('\n');
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
const int maxn=3e4+10;
const int maxa=1e6+10;
const int maxm=2e5+10;
struct node{
    int l,r,id;
    bool operator < (const node &t) const {
        return r<t.r;
    }
}t[maxm];
int n,m,a[maxn];
#define mid ((l+r)>>1)
#define lson rt*2,l,mid
#define rson rt*2+1,mid+1,r
int sum[maxn<<2],lazy[maxn<<2];
void pushdown(int rt,int l,int r){
    sum[rt*2]+=(mid-l+1)*lazy[rt];
    sum[rt*2+1]+=(r-mid)*lazy[rt];
    lazy[rt*2]+=lazy[rt];
    lazy[rt*2+1]+=lazy[rt];
    lazy[rt]=0;
}
void update(int rt,int l,int r,int L,int R,int x){
    if(L<=l && r<=R){
        sum[rt]+=(r-l+1)*x;
        lazy[rt]+=x;
        return;
    }
    else{
        if(lazy[rt])pushdown(rt,l,r);
        if(L<=mid)update(lson,L,R,x);
        if(R>=mid+1)update(rson,L,R,x);
        sum[rt]=sum[rt*2]+sum[rt*2+1];
    }
}
int query(int rt,int l,int r,int L,int R){
    if(L<=l && r<=R)return sum[rt];
    int ret=0;
    if(lazy[rt])pushdown(rt,l,r);
    if(L<=mid)ret+=query(lson,L,R);
    if(R>=mid+1)ret+=query(rson,L,R);
    return ret;
}
int ans[maxm];
vector<int>v[maxa];
void work(){
    int k=1;
    for(int i=1;i<=n && k<=m;i++){
        int c=a[i];
        if(v[c].size()==0)v[c].push_back(0);
        v[c].push_back(i);
        int s=v[c].size()-1;
        int pos1=v[c][s-1]+1,pos2=v[c][s];//将c颜色的区间加一从pos1更新到pos2
        update(1,1,n,pos1,pos2,1);
        while(t[k].r==i){
            ans[t[k].id]=query(1,1,n,t[k].l,t[k].l);//直接查询答案就可以
            ++k;
        }
    }
}
int main(){
    File();
    read(n);
    REP(i,1,n)read(a[i]);
    read(m);
    REP(i,1,m){
        read(t[i].l);
        read(t[i].r);
        t[i].id=i;
    }
    sort(t+1,t+m+1);
    work();
    REP(i,1,m)write(ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值