题意:给两个数列A, B,长度分别为n1, n2,保证A中每个元素互不相同,保证B中每个元素互不相同
。。进行Q次询问,每次查找A[l1…r1]和B[l2..r2]的交集 集合 大小是多少。。
比如 A = {1,2,3,4,5,6,7},B = {7,6,5,4,3,2,1}
查询A[2..4]和B[3..5]。。A[2..4] = {2,3,4};B[3..5] = {5,4,3},交集为{3,4},大小为2。。
链接:http://acm.uestc.edu.cn/#/problem/show/1104
做法:可以用离线树状数组来做,,下面只讲在线的方法,,关键是,保证了A,B中元素互不相同(曾记得只要保证一个可以了,现在认为要保证两个,可能要修改),,我们对于A的每个前缀序列,都建立一棵线段树。
假设当前A的前缀为preA,preA有一棵线段树T,线段树T(x)=1,表示B中位置为x的数存在preA中,线段树上 ∑Tbi=a 表示B中位置在[a,b]中的数出现在preA中的次数,那么这个出现次数是满足加减性的。我需要查询A中[a,b]和B中[c,d]的公共数的个数,就只需查询Sumb,Sumb表示B中位置在[c,d]中出现的数在A的前缀[1..b]中出现的次数,以及Sum(a-1),Sum(a-1)表示B中位置在[c,d]中出现的数在A的前缀[1..a)中出现的次数,那么,Sumb-Sum(a-1)即为,B中位置在[c,d]中出现的数在A中[a,b]中出现的次数。。
对于每个前缀序列都建一棵线段树,这个就用主席树来实现。和其他题目一样,先建一棵完整的空线段树,第1个前缀的线段树来自于空树,第i ( i≥2 ) 个前缀的线段树来自于第(i-1)个前缀的线段树。。建一棵树的代价是log的,查询是log的,总复杂度是log的。。
代码:
//Hello. I'm Peter.
#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<ctime>
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
#define N 200010
int n1, n2, a1[N], a2[N], m, mapto[N];
struct Data{
int id, x;
}d1[N], d2[N];
bool cmp1(const Data a, const Data b){
return a.x < b.x;
}
#define MAXN N * 22
int lch[MAXN], rch[MAXN], val[MAXN], T[N], nnod;
int plant(int l,int r){
int t = ++nnod;
val[t] = 0;
if(l == r) return t;
int mid = (l + r) >> 1;
lch[t] = plant(l, mid);
rch[t] = plant(mid + 1, r);
return t;
}
int update(int id,int p,int l,int r,int v){
int t = ++nnod;
lch[t] = lch[id], rch[t] = rch[id], val[t] = val[id] + v;
if(l == r) return t;
int mid = (l + r) >> 1;
if(p <= mid) lch[t] = update(lch[t], p, l, mid, v);
else rch[t] = update(rch[t], p, mid + 1, r, v);
return t;
}
int query(int lid,int rid,int ql,int qr,int l,int r){
if(ql == l && qr == r) return val[rid] - val[lid];
int mid = (l + r) >> 1;
if(qr <= mid) return query(lch[lid], lch[rid], ql, qr, l, mid);
else if(mid < ql) return query(rch[lid], rch[rid], ql, qr, mid + 1, r);
else return query(lch[lid], lch[rid], ql, mid, l, mid) + query(rch[lid], rch[rid], mid + 1, qr, mid + 1, r);
}
int main(){
scanf("%d",&n1);
for(int i = 1; i <= n1; i++){
scanf("%d",a1 + i);
d1[i].x = a1[i], d1[i].id = i;
}
scanf("%d",&n2);
for(int i = 1; i <= n2; i++){
scanf("%d",a2 + i);
d2[i].x = a2[i], d2[i].id = i;
}
sort(d1 + 1, d1 + 1 + n1, cmp1);
sort(d2 + 1, d2 + 1 + n2, cmp1);
for(int i = 1; i <= n2; i++) mapto[i] = 0;
int t1 = 1, t2 = 1;
while(t1 <= n1 && t2 <= n2){
while(t1 <= n1 && t2 <= n2 && d1[t1].x < d2[t2].x) t1++;
while(t1 <= n1 && t2 <= n2 && d1[t1].x > d2[t2].x) t2++;
if(t1 <= n1 && t2 <= n2 && d1[t1].x == d2[t2].x){
mapto[d2[t2].id] = d1[t1].id;
t1++;
t2++;
}
}
nnod = 0;
T[0] = plant(1, n1);
for(int i = 1; i <= n2; i++){
if(mapto[i] == 0) T[i] = T[i-1];
else T[i] = update(T[i-1], mapto[i], 1, n1, +1);
}
scanf("%d",&m);
for(int im = 1; im <= m; im++){
int l1, r1, l2, r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
int ans = query(T[l2 - 1], T[r2], l1, r1, 1, n1);
printf("%d\n",ans);
}
return 0;
}