【bzoj3236】[Ahoi2013]作业【树套树 线段树套主席树】
题目链接
蒟蒻不会莫队,只好用树套树。
看完这道题,是不是想起了【bzoj2120】数颜色?
如果只是查询l~r区间内的不同数字的个数,就在每个位置记录一个pre值,代表前一个与它相同的位置。这样问题就转化为了l~r之间有多少个位置的pre值 < l,直接上主席树即可。
再带上一个权值要在a~b之间,怎么办?先想一个很暴力的做法。在上述做法的基础上再在外面套一棵线段树,也就是一棵线段树每个节点上套一棵主席树。不用说,肯定爆空间。
怎么办呢?蒟蒻博主苦恼了很久。感谢wyc大神提供思路!其实很简单,对于每次查询,在外层的线段树加一个标记。最后把线段树遍历一遍,建当前节点对应区间的主席树,然后一次性把所有在这个节点上的查询给搞定。这样空间就够用了。
时间复杂度:线段树有logn层,每一层有n个位置,每个位置要在权值树上用logn的时间插入一次,每个询问被拆分为logn个,然后拆分后的询问每个要用logn的时间查询,因此时间复杂度是O((n+m) log^2 n)。理论上比莫队优,但实际上。。。不知道。
然后是很短的代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100005,M=1000005;
int n,m,l,r,a,b,num[N],last[N],pre[N],Hash[N],ans[M][2];
int cnt,root[N],sum[N*20],lc[N*20],rc[N*20];
struct Query{
int a,b,l,id;
};
struct data{
int a,v;
bool operator < (const data &b) const{
return a<b.a;
}
}d[N];
vector<Query> q[N*4];
void addquery(int o,int l,int r,int L,int R,Query qry){
if(L<=l&&R>=r){
q[o].push_back(qry);
return;
}
int mid=(l+r)/2;
if(L<=mid){
addquery(o*2,l,mid,L,R,qry);
}
if(R>mid){
addquery(o*2+1,mid+1,r,L,R,qry);
}
}
void build(int y,int &x,int l,int r,int k){
x=++cnt;
sum[x]=sum[y]+1;
lc[x]=lc[y];
rc[x]=rc[y];
if(l==r){
return;
}
int mid=(l+r)/2;
if(k<=mid){
build(lc[y],lc[x],l,mid,k);
}else{
build(rc[y],rc[x],mid+1,r,k);
}
}
int query(int y,int x,int l,int r,int k){
if(!x||l==r){
return 0;
}
int mid=(l+r)/2;
if(k<=mid){
return query(lc[y],lc[x],l,mid,k);
}else{
return sum[lc[x]]-sum[lc[y]]+query(rc[y],rc[x],mid+1,r,k);
}
}
void solve(int o,int l,int r){
if(q[o].size()){
Hash[0]=0;
for(int i=l;i<=r;i++){
Hash[++Hash[0]]=num[i];
}
sort(Hash+1,Hash+Hash[0]+1);
int s=0;
for(int i=l;i<=r;i++){
d[++s].a=lower_bound(Hash+1,Hash+Hash[0]+1,num[i])-Hash;
d[s].v=pre[i];
}
sort(d+1,d+s+1);
for(int i=1;i<=s;i++){
build(root[i-1],root[i],0,n,d[i].v);
}
int a,b;
for(int i=0;i<q[o].size();i++){
a=lower_bound(Hash+1,Hash+Hash[0]+1,q[o][i].a)-Hash;
b=upper_bound(Hash+1,Hash+Hash[0]+1,q[o][i].b)-Hash-1;
ans[q[o][i].id][0]+=sum[root[b]]-sum[root[a-1]];
ans[q[o][i].id][1]+=query(root[a-1],root[b],0,n,q[o][i].l);
}
memset(root,0,sizeof(int)*(Hash[0]+1));
memset(sum,0,sizeof(int)*(cnt+1));
memset(lc,0,sizeof(int)*(cnt+1));
memset(rc,0,sizeof(int)*(cnt+1));
cnt=0;
}
if(l==r){
return;
}
int mid=(l+r)/2;
solve(o*2,l,mid);
solve(o*2+1,mid+1,r);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
pre[i]=last[num[i]];
last[num[i]]=i;
}
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&l,&r,&a,&b);
addquery(1,1,n,l,r,(Query){a,b,l,i});
}
solve(1,1,n);
for(int i=1;i<=m;i++){
printf("%d %d\n",ans[i][0],ans[i][1]);
}
return 0;
}