CF1093E Intersection of Permutations 树状数组套权值线段树

\(\color{#0066ff}{ 题目描述 }\)

给定整数 \(n\) 和两个 \(1,\dots,n\) 的排列 \(a,b\)

\(m\) 个操作,操作有两种:

  • \(1\ l_a\ r_a\ l_b\ r_b\),设 \(a\)\([l_a;r_a]\) 区间内的元素集合为 \(S_a\),设 \(b\)\([l_b;r_b]\) 区间内的元素集合为 \(S_b\),求 \(\lvert S_a \bigcap S_b \rvert\)
  • \(2\ x\ y\),交换 \(b\) 的第 \(x\) 位与第 \(y\) 位。

\(1 \le n,m \le 2 \cdot 10^5\)

\(\color{#0066ff}{输入格式}\)

第一行,两个整数 \(n,m\) 以下两行,每行 \(n\) 个整数,分别表示 \(a,b\)。 以下 \(m\) 行,每行一个操作。

\(\color{#0066ff}{输出格式}\)

对于每个 \(1\) 操作,输出答案。

\(\color{#0066ff}{输入样例}\)

6 7
5 1 4 2 3 6
2 5 3 1 4 6
1 1 2 4 5
2 2 4
1 1 2 4 5
1 2 3 3 5
1 1 6 1 2
2 4 1
1 4 4 1 3

\(\color{#0066ff}{输出样例}\)

1
1
1
2
0

\(\color{#0066ff}{数据范围与提示}\)

\(2\leq n \leq 2*10^5, 1\leq m \leq 2*10^5\)

\(\color{#0066ff}{ 题解 }\)

对于排列的每个元素,都有在a中出现的位置pa,在b中出现的位置pb

将其当做二维点(pa,pb),那么其实这题就建好模了

给你二维平面上的一些点

有交换两个点的y坐标操作

每次查询一个矩形内有多少点

树套树

树状数组套权值线段树

树状数组维护pb, 权值线段树维护对应区间的pa

在权值线段树上找到对应区间返回

要写数组版,而且还得内存回收,作为指针选手,以哭晕qwq

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; int x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 1e6 * 40;
const int N = 2e5 + 10;
struct node {
    int num;
    int ch[2];
    int &operator [] (const int &b) {
        return ch[b];
    }
    node(int a = 0): num(a) {
        ch[0] = ch[1] = 0;
    }
}e[maxn];
int a[N], b[N], pa[N], pd[N], cnt;
int root[N];
int n, m;
int sta[N], top;
int low(int x) { return (x) & (-x); }
int newnode() {
    return top? sta[top--] : ++cnt;
}
void add(int &now, int pos, int k, int l, int r) {
    if(!now) now = newnode();
    e[now].num += k;
    if(l == r) return;
    int mid = (l + r) >> 1;
    if(pos <= mid) add(e[now][0], pos, k, l, mid);
    else add(e[now][1], pos, k, mid + 1, r);
    if(!e[now].num) sta[++top] = now, now = 0;
}
void add(int pos, int val, int k) {
    for(int i = pos; i <= n; i += low(i))
        add(root[i], val, k, 1, n);
}
int query(int L, int R, int now, int l, int r) {
    if(!now) return 0;
    if(L <= l && r <= R) return e[now].num;
    int mid = (l + r) >> 1, tot = 0;
    if(L <= mid) tot += query(L, R, e[now][0], l, mid);
    if(R > mid) tot += query(L, R, e[now][1], mid + 1, r);
    return tot;
}
int query(int L, int R, int l, int r) {
    int ans = 0;
    for(int i = L - 1; i; i -= low(i)) ans -= query(l, r, root[i], 1, n);
    for(int i = R; i; i -= low(i)) ans += query(l, r, root[i], 1, n);
    return ans;
}
int main() {
    n = in(), m = in();
    for(int i = 1; i <= n; i++) a[i] = in(), pa[a[i]] = i;
    for(int i = 1; i <= n; i++) b[i] = pa[in()];
    for(int i = 1; i <= n; i++) add(i, b[i], 1);
    int flag, l, r, L, R;
    while(m --> 0) {
        flag = in();
        if(flag == 1) {
            L = in(), R = in(), l = in(), r = in();
            printf("%d\n", query(l, r, L, R));
        }
        else {
            l = in(), r = in();
            add(l, b[l], -1);
            add(r, b[r], -1);
            add(l, b[r], 1);
            add(r, b[l], 1);
            std::swap(b[l], b[r]);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/olinr/p/10227230.html