【cdq分治】【CF1093E】 Intersection of Permutations

传送门

果然前两天写完咕咕咕那个题的题解以后博客就开始咕咕咕了……

Description

给定整数 \(n\) 和两个 \(1~\sim~n\) 的排列 \(A,B\)

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

  • \(1~,~l1~,~r1~,~l2~,~r2\)\((\bigcup_{i=l1}^{r1} A_i)~\bigcap~(\bigcup_{i=l2}^{r2} B_i)\)
  • \(2~,~x~,~y\) 交换 \(B_x,~B_y\)

Input

第一行是两个正整数 \(n,m\)

下面两行,每行 \(n\) 个数, 给出排列 \(A,B\)

下面 \(m\) 行,每行一个操作

Output

对每个询问输出一行代表答案

Hint

\(0~\leq~n,m~\leq~2~\times~10^5\)

Solution

这不是hash+二维树状数组好题嘛!

然而因为一次操作有 \(4\) 次查询,相当于操作次数 \(10^6\),hash树状数组显然过不去= =

考虑如果给 \(B\) 重编号,\(B_i\) 代表原 \(B\) 中第 \(i\) 个元素在 \(A\) 中出现的位置,那么每次查询就等价于区间 \([l2, r2]\) 中有多少个数在 \([l1,r1]\) 内。于是这个问题被转化成了一个二维数点问题那我们去写hash+树状数组吧!,并且资瓷离线,于是考虑cdq分治过掉

这里记录一下待修改的cdq怎么写:将一次修改操作改为一个添加操作和一个删除操作。例如,交换 \(x, y\) 等价于删除 \((x,B_x),(y,B_y)\),并且加入 \((x,B_y),(y,B_x)\)。初始的序列以添加的形式给出

于是复杂度 \(O(n\log^2n)\),可以过掉本题

Code

#include <cstdio>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long

typedef long long int ll;

namespace IPT {
    const int L = 1000000;
    char buf[L], *front=buf, *end=buf;
    char GetChar() {
        if (front == end) {
            end = buf + fread(front = buf, 1, L, stdin);
            if (front == end) return -1;
        }
        return *(front++);
    }
}

template <typename T>
inline void qr(T &x) {
    rg char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    if (lst == '-') x = -x;
}

template <typename T>
inline void ReadDb(T &x) {
    rg char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
    if (ch == '.') {
        ch = IPT::GetChar();
        double base = 1;
        while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
    }
    if (lst == '-') x = -x;
}

namespace OPT {
    char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
    if (x < 0) {x = -x, putchar('-');}
    rg int top=0;
    do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
    while (top) putchar(OPT::buf[top--]);
    if (pt) putchar(aft);
}

const int maxn = 200010;
const int maxt = 1000010;

struct Zay {
    int id, x, y, mul, oppt, vec;
    
    inline void setit(ci _a, ci _b, ci _c, ci _d, ci _e, ci _f) {
        id = _a; x = _b; y = _c; mul = _d; oppt = _e; vec = _f;
    }
};
Zay opt[maxt], temp[maxt];

inline int lowbit(ci &x) {return x & -x;}

int n, m, cnt, tot;
int CU[maxn], MU[maxn], ans[maxn], tree[maxt];

int ask(int);
void cdq(ci, ci);
void add(int, ci);
void query(const Zay&);
void update(const Zay&);

int main() {
    freopen("1.in", "r", stdin);
    qr(n); qr(m);
    int a, l1, r1, l2, r2;
    for (rg int i = 1; i <= n; ++i) {
        a = 0; qr(a); CU[a] = i;
    }
    for (rg int i = 1; i <= n; ++i) {
        a = 0; qr(a);// printf("QWQ%d\n", a);
        ++cnt; opt[cnt] = (Zay){opt[cnt - 1].id + 1, i, MU[i] = CU[a], 1, 0, 0};
    }
    while (m--) {
        a = 0; qr(a);//printf("EM%d\n", a);
        if (a == 1) {
            l1 = r1 = l2 = r2 = 0; qr(l1); qr(r1); qr(l2); qr(r2); --l1; --l2;
            int _pre = opt[cnt].id + 1;
            opt[++cnt].setit(_pre, r2, r1, 1, 1, ++tot);
            opt[++cnt].setit(_pre, l2, r1, -1, 1, tot);
            opt[++cnt].setit(_pre, l2, l1, 1, 1, tot);
            opt[++cnt].setit(_pre, r2, l1, -1, 1, tot);
        } else {
            l1 = r1 = 0; qr(l1); qr(r1);
            int _pre = opt[cnt].id + 1;
            opt[++cnt].setit(_pre, l1, MU[l1], -1, 0, 0);
            opt[++cnt].setit(_pre, r1, MU[r1], -1, 0, 0);
            std::swap(MU[l1], MU[r1]);
            opt[++cnt].setit(_pre, l1, MU[l1], 1, 0, 0);
            opt[++cnt].setit(_pre, r1, MU[r1], 1, 0, 0);
        }
    }
    cdq(1, cnt);
    for (rg int i = 1; i <= tot; ++i) qw(ans[i], '\n', true);
    return 0;
}

void cdq(ci l, ci r) {
    if (l == r) return;
    int mid = (l + r) >> 1;
    cdq(l, mid); cdq(mid + 1, r);
    for (rg int i = l, ll = l, rr = mid + 1; i <= r; ++i) {
        if (ll > mid) {
            query(opt[rr]);
            temp[i] = opt[rr++];
        } else if (rr > r) {
            update(opt[ll]);
            temp[i] = opt[ll++];
        } else if (opt[ll].x <= opt[rr].x) {
            update(opt[ll]);
            temp[i] = opt[ll++];
        } else {
            query(opt[rr]);
            temp[i] = opt[rr++];
        }
    }
    for (rg int i = l; i <= mid; ++i) if (!opt[i].oppt) {
        add(opt[i].y, -opt[i].mul);
    }
    for (rg int i = l; i <= r; ++i) opt[i] = temp[i];
}

inline void update(const Zay &_t) {
    if (_t.oppt) return;
    add(_t.y, _t.mul);
}

inline void query(const Zay &_t) {
    if (!_t.oppt) return;
    ans[_t.vec] += _t.mul * ask(_t.y);
}

void add(int x, ci v) {
    while (x <= n) {
        tree[x] += v;
        x += lowbit(x);
    }
}

int ask(int x) {
    int _ret = 0;
    while (x) {
        _ret += tree[x];
        x -= lowbit(x);
    }
    return _ret;
}

Summary

cdq处理带修问题时,将修改变为删除和插入。

转载于:https://www.cnblogs.com/yifusuyi/p/10158773.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值