owned this note
owned this note
Published
Linked with GitHub
# Range Distinct values queries
Judge Time:分塊(550ms)>持久化線段樹(310ms)>BIT(270ms)
## 分塊-莫隊算法
詳見[分塊演算法](/86kWJsh6S5KjPzYLc1xsxg)
:::spoiler code
```cpp=
#pragma GCC optimize("Ofast,unroll-loops")
#pragma GCC optimize("O3")
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define pii pair<int,int>
#define F first
#define S second
#define pb push_back
#define all(a) (a).begin(),(a).end()
#define mem(a) memset(a,0,sizeof(a))
#define binary_search
#define inf LLONG_MAX
#define mod 1000000007
#define MAXN 1000005
int N=1,n,m,x,k;
//-----------------------------------
int a[MAXN];
struct Q{
int F,S,i;
};
bool cmp(Q a,Q b){
if(a.F/k==b.F/k){
if((a.F/k)%2) return a.S>b.S;
else return a.S<b.S;
}
return a.F/k<b.F/k;
}
int l,r,cnt;
int mp[MAXN];
void sub(int x){
mp[a[x]]--;
if(!mp[a[x]]) cnt--;
}
void add(int x){
if(!mp[a[x]]) cnt++;
mp[a[x]]++;
}
int query(int lb,int rb){
while(l<lb) sub(l++);
while(l>lb) add(--l);
while(r>rb) sub(r--);
while(r<rb) add(++r);
return cnt;
}
void solve(){
int q;
cin >> n;
k=sqrt(n)+1;
for(int i=1;i<=n;i++){
cin >> a[i];
}
cin >> q;
vector<Q> que(q);
for(int i=0;i<q;i++){
cin >> que[i].F >> que[i].S;
que[i].i=i;
}
sort(all(que),cmp);
vector<int> ans(q);
for(int i=0;i<q;i++){
ans[que[i].i]=query(que[i].F,que[i].S);
}
for(auto i:ans) cout << i << '\n';
}
signed main(){
cin.sync_with_stdio(0),cin.tie(0);
//cin >> N;
for(int Case=1;Case<=N;Case++){
solve();
}
}
```
:::
---------------------
記錄在[1,i]區間出現的同一個數中最右邊的index,然後設一個cnt陣列,把這個index的位置+1,這樣在查詢區間[l,r]的時候就等於在cnt中,把r的prefix_sum-l的prefix_sum就是答案
## BIT
那既然要計算前綴和,BIT是很好的選擇,但只能離線處理
作法:先照右界排序,確保單調性以及線性複雜度,在插入時,如果a_i已被紀錄,則先把原本a_i的position刪除,再新增當前position
=>速度快,離線
:::spoiler code
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define pii pair<int,int>
#define F first
#define S second
#define pb push_back
#define all(a) (a).begin(),(a).end()
#define mem(a) memset(a,0,sizeof(a))
#define bs binary_search
inline void print(vector<int> &arr){for(auto &i:arr) cout << i << '\n';}
inline void read(vector<int> &arr){for(auto &i:arr) cin >> i;}
const int MAXN=4e5+5;
const int inf=1e18+5;
const int mod=1e9+7;
int N=1,n,m,k,ans;
//-----------------------------------
int L,R,cnt,a[MAXN],ins[MAXN];
struct node{
int l,r,idx;
bool operator<(node b){
return r<b.r;
}
};
int bit[MAXN];
void update(int x,int v){
x++;
while(x<MAXN){
bit[x]+=v;
x+=x&-x;
}
}
int query(int x){
x++;
int ans=0;
while(x){
ans+=bit[x];
x-=x&-x;
}
return ans;
}
//check initialization and MAXN
void solve(){
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
int q;
cin >> q;
vector<node> Q(q);
for(int i=0,l,r;i<q;i++){
cin >> l >> r;
Q.pb({l,r,i});
}
sort(all(Q));
vector<int> ans(q);
int now=0;
for(auto i:Q){
while(now<=i.r){
if(ins[a[now]]) update(ins[a[now]],-1);
update(now,1);
ins[a[now]]=now;
now++;
}
ans[i.idx]=query(i.r)-query(i.l-1);
}
print(ans);
}
signed main(){
cin.sync_with_stdio(0),cin.tie(0);
//cin >> N;
for(int Case=1;Case<=N;Case++){
ans=0;
solve();
}
}
```
:::
## 持久化線段樹
強制在線就要用到持久化線段樹,其概念是相同的,只是做法有些不同
作法:對於每個i都建立一顆[1,i]線段樹,查詢的時候就去找右界相應的線段樹,在該顆線段樹中遞迴查詢
=>速度慢,在線
:::spoiler code
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define pii pair<int,int>
#define F first
#define S second
#define pb push_back
#define all(a) (a).begin(),(a).end()
#define mem(a) memset(a,0,sizeof(a))
#define bs binary_search
inline void print(vector<int> &arr){for(auto &i:arr) cout << i << '\n';}
inline void read(vector<int> &arr){for(auto &i:arr) cin >> i;}
const int MAXN=1e6+5;
const int inf=1e18+5;
const int mod=1e9+7;
int N=1,n,m,k,ans;
//-----------------------------------
int lsun[MAXN<<2],rsun[MAXN<<2],sum[MAXN<<2],a[MAXN],root[MAXN],used[MAXN],idx;
int update(int l,int r,int pre,int target,int value){
int now=++idx;
lsun[now]=lsun[pre];
rsun[now]=rsun[pre];
sum[now]=sum[pre]+value;
if(l==r) return now;
int mid=(l+r)>>1;
if(target<=mid){
lsun[now]=update(l,mid,lsun[now],target,value);
}else{
rsun[now]=update(mid+1,r,rsun[now],target,value);
}
return now;
}
int query(int node,int l,int r,int target){
if(l==r) return sum[node];
int mid=(l+r)>>1;
if(target<=mid) return sum[rsun[node]]+query(lsun[node],l,mid,target);
else return query(rsun[node],mid+1,r,target);
}
//check initialization and MAXN
void solve(){
cin >> n;
for(int i=1;i<=n;i++){
cin >> a[i];
if(used[a[i]]){
root[i]=update(1,n,root[i-1],used[a[i]],-1);
root[i]=update(1,n,root[i],i,1);
}else{
root[i]=update(1,n,root[i-1],i,1);
}
used[a[i]]=i;
}
int q;
cin >> q;
while(q--){
int lb,rb;
cin >> lb >> rb;
cout << query(root[rb],1,n,lb) << '\n';
}
}
signed main(){
cin.sync_with_stdio(0),cin.tie(0);
//cin >> N;
for(int Case=1;Case<=N;Case++){
ans=0;
solve();
}
}
```
:::