三维偏序-陌上花开

三维偏序-陌上花开

  原题传送门:洛谷P3810 三维偏序(陌上花开)

题目大意

  这是很经典的一道题了,并且可以用很多算法来解决。

  有 n n n 个元素每个元素 x i x_i xi a i , b i , c i a_i, b_i, c_i ai,bi,ci 三个属性。设 f ( i ) f(i) f(i) 表示满足 ( a j ≤ a i ) ∧ ( b j ≤ b i ) ∧ ( c j ≤ c i ) ∧ ( i ≠ j ) (a_j \leq a_i) \land (b_j \leq b_i) \land (c_j \leq c_i) \land (i \neq j) (ajai)(bjbi)(cjci)(i=j) j j j 的数量。

  对于 d ∈ [ 0 , n ) d \in [0, n) d[0,n),求满足 f ( i ) = d f(i) = d f(i)=d i i i 的数量。

树状数组套权值线段树

不要告诉我你不会树状数组和线段树

  首先我们要知道,对于一个数据结构我们只能处理出一个维度的信息,也就是说如果现在每个元素 x i x_i xi 只有一个信息为 a i a_i ai 的话,那我们只用写一个裸的树状数组就能过掉这个题。那如果是每个 x i x_i xi 有两个信息 a i a_i ai b i b_i bi 的话那我们只需要先对于第一维的信息 a i a_i ai 进行排序然后依次加进树状数组中(用树状数组维护第二维信息 b i b_i bi),这样在每次处理的时候就自然统计的就是满足比当前 a i a_i ai 小的中比 b i b_i bi 小的了。

  但是现在有三个维度的信息,所以我们就要引入树套树这个东西了,我们先对 a i a_i ai 进行排序,然后一次处理就能解决掉第一维。然后我们再用树状数组套线段树来维护二维和三维。

  顾名思义,树状数组套线段树的意思就是我们要树状数组的每一个节点上都维护一颗线段树,这个树状数组的节点上的值就代表第二维 b i b_i bi 的信息,然后在这个节点上的线段树中插入第三维 c i c_i ci 的信息。在每次查询的时候对于每一个树状数组的节点都进行一遍线段树的查询。所以这样的时间复杂度就是 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n) 的,是可以过掉这个题的。

  但是还有一个问题,就是空间复杂度。我们知道单个线段树或者树状数组的空间复杂度都是 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的,所以如果我们真的像上文说的那样,对于每一个树状数组的节点都弄一颗线段树的话那么空间复杂度就会变成 O ( n 2 log ⁡ 2 n ) O(n^2\log^2n) O(n2log2n),这样我们显然是存不下的。所以我们采用另外一种神奇的方法叫做动态开点。也就是说在每次修改的需要用到某个节点的时候再创建这个节点。显然每次修改只需要用到 log ⁡ n \log n logn 个点,所以我们这样做的空间复杂度就是 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n) 的。 然后让我们来看代码吧:

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 1001000
#define MAXK 2002000

inline int read(){
    int x = 0; char c = getchar();
    while(c < '0' or c > '9') c = getchar();
    while('0' <= c and c <= '9'){
        x = x * 10 + c - '0'; c = getchar();
    }
    return x;
}

int n = 0; int k = 0;
struct Tdata{
	int x, y, z;
	bool operator < (const Tdata &rhs) const {
		return x != rhs.x ? (x < rhs.x) : (y != rhs.y ? (y < rhs.y) : (z < rhs.z));
	}
	bool operator == (const Tdata & rhs) const {
		return (x == rhs.x) and (y == rhs.y) and (z == rhs.z);
	}
}data[MAXN];
int ans[MAXN] = { 0 };

struct Tnode{
    int dat;
    int ls, rs;
}t[MAXN >> 10];
int tot = 0;

struct SegTree{

    inline void up(int p) { t[p].dat = t[t[p].ls].dat + t[t[p].rs].dat; }

    void update(int &p, int l, int r, int x, int val){
        if(!p) p = ++tot;
        if(l == r) { t[p].dat += val; return; }
        int mid = (l + r) >> 1;
        if(x <= mid) update(t[p].ls, l, mid, x, val);
        else update(t[p].rs, mid + 1, r, x, val);
        up(p);
    }

    int query(int p, int l, int r, int ql, int qr){
        if(!p) p = ++tot;
        if(ql <= l and r <= qr) return t[p].dat;
        int mid = (l + r) >> 1; int ans = 0;
        if(ql <= mid) ans += query(t[p].ls, l, mid, ql, qr);
        if(qr > mid) ans += query(t[p].rs, mid + 1, r, ql, qr);
        return ans;
    }

}seg;

struct BIT{

    int root[MAXN >> 5];

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

    void add(int x, int y, int val){
        for(; x <= k; x += lowbit(x)) seg.update(root[x], 1, k, y, val);
    }

    int query(int x, int y){
        int ans = 0;
        for(; x; x -= lowbit(x)) ans += seg.query(root[x], 1, k, 1, y);
        return ans;
    }

}T;

int main(){
    n = in; k = in;
    for(int i = 1; i <= n; i++) data[i].x = in, data[i].y = in, data[i].z = in;
    sort(data + 1, data + n + 1); int sum = 1;
    for(int i = 1; i <= n; i++) {
        if(data[i] == data[i + 1]) { sum++; continue; }
        T.add(data[i].y, data[i].z, sum);
        int res = T.query(data[i].y, data[i].z);
        ans[res] += sum; sum = 1;
    }
    for(int i = 1; i <= n; i++) cout << ans[i] << '\n';
    return 0;
}

CDQ 分治

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值