【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

时间: 2024-08-04 21:03:51

【cdq分治】【CF1093E】 Intersection of Permutations的相关文章

CF1093E [Intersection of Permutations]

题意 给定整数n两个1-n的排列a,b,有m次操作:若opt==1,则有 l1 r1 l2 r2,求出a:[l1,r1]与b:[l2,r2]中相同元素的交的大小.若opt==2,则有 x y,交换b排列中的 第x位与第y位. n,m≤200,000 思考 只改变b排序中元素位置,考虑每次询问a中l1-r1的元素何时对答案有贡献. 设a中值为i的位置为pi(下标从1开始),b中值为i的位置为qi,则当且仅当[l1,r1]覆盖pi且[l2,r2]覆盖qi时,对答案有贡献. 再换个角度,若只考虑qi,

Cideforces 1093E Intersection of Permutations (CDQ分治+树状数组)

---恢复内容开始--- 题意:给你两个数组a和b,a,b都是一个n的全排列:有两种操作:一种是询问区间在数组a的区间[l1,r1]和数组b的区间[l2,r2]出现了多少相同的数字,另一种是交换数组b中x位置和y位置的数字. 思路:我们可以建立数组b对数组a的映射mp,mp[x]表示数组b中x位置的数在数组a中出现的位置,这样问题转化为了带修改的询问一个矩形内点的个数的问题.想法是树套树,但是这题卡常,很多树套树会被卡掉,介于本辣鸡的代码能力,很容易写丑,所以用CDQ分治. 此问题和三维偏序问题

HDU5322 Hope(DP + CDQ分治 + NTT)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5322 Description Hope is a good thing, which can help you conquer obstacles in your life, just keep fighting, and solve the problem below. In mathematics, the notion of permutation relates to the ac

CDQ分治与整体二分小结

前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完蛋.jpg 绝望.jpg. 关于CDQ分治 CDQ分治,求的是三维偏序问题都知道的. 求法呢,就是在分治外面先把一维变成有序 然后分治下去,左边(l,mid)关于右边(mid+1,r)就不存在某一维的逆序了,所以只有两维偏序了. 这个时候来一波"树状数组求逆序对"的操作搞一下二维偏序 就可

【BZOJ3963】[WF2011]MachineWorks cdq分治+斜率优化

[BZOJ3963][WF2011]MachineWorks Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先进的机械设备生产先进的机器.原来的那一台生产机器已经坏了,所以你要去为公司买一台新的生产机器.你的任务是在转型期内尽可能得到更大的收益.在这段时间内,你要买卖机器,并且当机器被ACM公司拥有的时候,操控这些机器以获取利润.因为空间的限制,ACM公司在任何时候都只能最多拥有一台机器. 在转型期内,有若

BZOJ 2225 [Spoj 2371]Another Longest Increasing(CDQ分治)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2225 [题目大意] 给定N个数对(xi,yi),求最长上升子序列的长度. 上升序列定义为{(xi,yi)}满足对i<j有xi<xj且yi<yj. [题解] CDQ分治,将每个区间按照a排序,用区间左边的数据来更新右边的最长上升序列, 为排除a相等但是b上升情况的误统计,在排序时加入下标作为第二关键字, 使得a相等的情况下标小的后更新. [代码] #include <cs

BZOJ 3262: 陌上花开 [CDQ分治 三维偏序]

Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb.显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值. 以下N行,每

HDU 5618:Jam&#39;s problem again(CDQ分治+树状数组处理三维偏序)

http://acm.hdu.edu.cn/showproblem.php?pid=5618 题意:-- 思路:和NEUOJ那题一样的.重新写了遍理解了一下,算作处理三维偏序的模板了. 1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #d

ACdream1157 Segments(CDQ分治 + 线段树)

题目这么说的: 进行如下3种类型操作:1)D L R(1 <= L <= R <= 1000000000) 增加一条线段[L,R]2)C i (1-base) 删除第i条增加的线段,保证每条插入线段最多插入一次,且这次删除操作一定合法3) Q L R(1 <= L <= R <= 1000000000) 查询目前存在的线段中有多少条线段完全包含[L,R]这个线段,线段X被线段Y完全包含即LY <= LX <= RX <= RY) 初学CDQ分治是看了B