【題解】洛谷:P4396 [AHOI2013] 作業(值域分塊)

sad_lin發表於2024-11-18

P4396 [AHOI2013] 作業

值域分塊入門題,其實和值域線段樹一樣道理,就是在值域上分塊。

發現有兩個限制:數列區間和值域區間,但是發現第二問很像求區間不同的數的個數,直接莫隊,但是我們還要對值域分塊,我們維護值域塊的某個數的出現次數以及塊內總和,然後查詢時就是普通的分塊區間和做法。

#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pir pair<int,int>
#define re register
const int inf=1e9;
const int N=4e5+10;
const int mod=1e9+7;
using namespace std;

int n,m;
int a[N];

int v;

struct ss{
	int l,r,a,b,id;
}q[N];

int len,lenv;
int of[N],ofv[N];

int sum[N];//值域上單個數 
int sumv[N];//值域塊內個數
int summ[N];//值域塊內不重複個數 

void del(int x){
	sum[a[x]]--;
	sumv[ofv[a[x]]]--;
	if(sum[a[x]]<=0){
		summ[ofv[a[x]]]--;
	} 
}

void add(int x){
	sum[a[x]]++;
	sumv[ofv[a[x]]]++;
	if(sum[a[x]]<=1){
		summ[ofv[a[x]]]++;
	} 
}

int ans1[N],ans2[N];

int getans1(int a,int b){
	int ans=0;
	b=min(b,v);
	if(a>v){
		return 0;
	}
	if(ofv[a]==ofv[b]){
		for(int i=a;i<=b;i++){
			ans+=sum[i];
		}
		return ans;
	}
	
	for(int i=ofv[a]+1;i<=ofv[b]-1;i++){
		ans+=sumv[i];
	}
	for(int i=a;ofv[i]==ofv[a]&&a<=v;i++){
		ans+=sum[i];
	}
	for(int i=b;ofv[i]==ofv[b]&&b>=0;i--){
		ans+=sum[i];
	}
	return ans;
}

int getans2(int a,int b){//不同個數 
	int ans=0;
	b=min(b,v);
	if(a>v){
		return 0;
	}
	if(ofv[a]==ofv[b]){
		for(int i=a;i<=b;i++){
			ans+=(bool) sum[i];
		}
		return ans;
	}
	
	for(int i=ofv[a]+1;i<=ofv[b]-1;i++){
		ans+=summ[i];
	}
	for(int i=a;ofv[i]==ofv[a]&&a<=v;i++){
		ans+=(bool)sum[i];
	}
	for(int i=b;ofv[i]==ofv[b]&&b>=0;i--){
		ans+=(bool)sum[i];
	}
	return ans;
}

bool cmp(ss g,ss h){
	return (of[g.l]^of[h.l])?of[g.l]<of[h.l]:((of[g.l]&1)?g.r<h.r:g.r>h.r);
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr); 
	
	cin>>n>>m;
	
	len=1.0*n/sqrt(m)+1;
	
	for(int i=1;i<=n;i++){
		cin>>a[i];
		v=max(v,a[i]);
		of[i]=(i-1)/len+1;
	}
	for(int i=1;i<=m;i++){
		cin>>q[i].l>>q[i].r>>q[i].a>>q[i].b;
		q[i].id=i;
	}
	
	lenv=1.0*sqrt(v)+1;
	
	for(int i=1;i<=v;i++){
		ofv[i]=(i-1)/lenv+1;
	}
	int l=1,r=0;
	
	sort(q+1,q+1+m,cmp);
	
	for(int i=1;i<=m;i++){
		int ql=q[i].l,qr=q[i].r;
		
		while(l<ql) del(l++);
		while(l>ql)	add(--l);
		while(r<qr) add(++r);
		while(r>qr)	del(r--);
		
		ans1[q[i].id]=getans1(q[i].a,q[i].b);
		ans2[q[i].id]=getans2(q[i].a,q[i].b);
	}
	
	for(int i=1;i<=m;i++){
		cout<<ans1[i]<<" "<<ans2[i]<<"\n";
	}
	return 0;
}

相關文章