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