CODJ 1104 求两个数列的子列的交集(主席树或离线树状数组)

题意:给两个数列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 ( i2 ) 个前缀的线段树来自于第(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;
}

你可能感兴趣的:(主席树)