---
title: Karen and Cards
tags:
  - Segment Tree
id: "828"
categories:
  - - OI
    - Data Structure
date: 2017-06-29 01:10:48
---

## 题目描述

Karen just got home from the supermarket, and is getting ready to go to sleep.

After taking a shower and changing into her pajamas, she looked at her shelf and saw an album. Curious, she opened it and saw a trading card collection.

She recalled that she used to play with those cards as a child, and, although she is now grown-up, she still wonders a few things about it.

Each card has three characteristics: strength, defense and speed. The values of all characteristics of all cards are positive integers. The maximum possible strength any card can have is $p$, the maximum possible defense is $q$ and the maximum possible speed is $r$.

There are $n$ cards in her collection. The $i$-th card has a strength $a_i$, defense $b_i$ and speed $c_i$, respectively.

A card beats another card if at least two of its characteristics are strictly greater than the corresponding characteristics of the other card.

She now wonders how many different cards can beat all the cards in her collection. Two cards are considered different if at least one of their characteristics have different values.

## 题意概述

给定$n$张牌，每张牌有三个属性$a_i, b_i, c_i$。一张牌能压制另一张牌$\Leftrightarrow$前者的至少两个属性严格大于后者的对应属性。已知三个属性的上限分别为$p, q, r$，问有多少种不同的牌能压制给定的$n$张牌。

数据范围：$1 \le n, p, q, r \le 5 \times 10^5, \; 1 \le a_i \le p, \; 1 \le b_i \le q, \; 1 \le c_i \le r$。

## 算法分析

可以先枚举从$1$到$p$枚举$a$属性，分别算出可行的牌数，累加得到答案。

现在考虑$b, c$属性。设正在枚举$a=k$的情况。我们以$b$为$x$轴、$c$为$y$轴建立平面直角坐标系。对于$a_i \ge k$的牌，必须满足$b_i \lt x \land c_i \lt y$；对于$a_i \lt k$的牌，必须满足$b_i \lt x \lor c_i \lt y$。

![](1.png)

如图，红色区域表示$a_i \ge k$的牌可取的点，蓝色、绿色区域表示$a_i \lt k$的牌不可取的点。显然，用红色区域的点数减去红色和蓝色、绿色区域交集的点数，就是$a=k$时可行的牌数。

红色区域的大小取决于$a_i \ge k$的牌中$b_i, c_i$的最大值。蓝色、绿色区域的大小取决于$a_i \lt k$的牌中$b_i, c_i$的值；这可以用线段树来维护。理论时间复杂度为$O(n\log n)$。

## 代码

```cpp
#include <cstdio>
#include <algorithm>
using namespace std;
struct card {
    long long a, b, c;
} c[500002];
bool operator < (card a, card b) {
    return a.a < b.a;
}
long long n, p, q, r, pnt = 1, ans, s[500002], t[500002];
struct segment_tree {
    struct node_type {
        long long l, r, val, max, sum, tag;
        node_type *child[2];
    } *root;
    void build_tree(node_type *root) {
        if (root->l == root->r) return;
        long long mid = root->l + root->r >> 1;
        node_type *p, *q;
        p = new node_type;
        p->l = root->l, p->r = mid, p->val = p->max = p->sum = p->tag = 0;
        q = new node_type;
        q->l = mid + 1, q->r = root->r, q->val = q->max = q->sum = q->tag = 0;
        build_tree(p), build_tree(q);
        root->child[0] = p, root->child[1] = q;
    }
    void push_up(node_type *root) {
        if (root->l != root->r) {
            root->sum = root->child[0]->sum + root->child[1]->sum;
            root->max = max(root->child[0]->max, root->child[1]->max);
        }
    }
    void push_down(node_type *root) {
        if (root->tag && root->l != root->r) {
            root->child[0]->tag = root->child[0]->max = root->child[1]->tag = root->child[1]->max = root->tag;
            root->child[0]->sum = (root->child[1]->l - root->child[0]->l) * root->tag;
            root->child[1]->sum = (root->child[1]->r - root->child[0]->r) * root->tag;
        }
        root->tag = 0;
    }
    void insert_line(node_type *root, long long l, long long r, long long t) {
        if (l == root->l && r == root->r) {
            root->tag = root->max = t;
            root->sum = (r - l + 1) * t;
            return;
        }
        push_down(root);
        if (r < root->child[1]->l) insert_line(root->child[0], l, r, t);
        else if (l > root->child[0]->r) insert_line(root->child[1], l, r, t);
        else {
            insert_line(root->child[0], l, root->child[0]->r, t);
            insert_line(root->child[1], root->child[1]->l, r, t);
        }
        push_up(root);
    }
    long long get(node_type *root, long long t) {
        if (root->l == root->r) return root->l;
        push_down(root);
        if (root->child[1]->max < t) return get(root->child[0], t);
        else return get(root->child[1], t);
    }
    long long get_sum(node_type *root, int l, int r) {
        if (l == root->l && r == root->r) return root->sum;
        push_down(root);
        if (r < root->child[1]->l) return get_sum(root->child[0], l, r);
        else if (l > root->child[0]->r) return get_sum(root->child[1], l, r);
        else return get_sum(root->child[0], l, root->child[0]->r) + get_sum(root->child[1], root->child[1]->l, r);
    }
} tree;
int main()
{
    scanf("%lld%lld%lld%lld", &n, &p, &q, &r);
    tree.root = new segment_tree::node_type;
    tree.root->l = 0, tree.root->r = q, tree.root->val = tree.root->max = tree.root->sum = tree.root->tag = 0;
    tree.build_tree(tree.root);
    for (int i = 1; i <= n; ++i) {
        scanf("%lld%lld%lld", &c[i].a, &c[i].b, &c[i].c);
    }
    sort(c + 1, c + n + 1);
    s[n] = c[n].b, t[n] = c[n].c;
    for (int i = n - 1; i; --i) {
        s[i] = max(s[i + 1], c[i].b);
        t[i] = max(t[i + 1], c[i].c);
    }
    tree.insert_line(tree.root, 0, 0, q + 1);
    long long tmp;
    for (int i = 1, j = 1; i <= p; ++i) {
        for (; j <= n && c[j].a < i; ++j) {
            tmp = tree.get(tree.root, c[j].c);
            if (tmp >= c[j].b) continue;
            tree.insert_line(tree.root, tmp + 1, c[j].b, c[j].c);
        }
        tmp = max(s[j], tree.get(tree.root, t[j] + 1));
        ans += (tmp - s[j]) * r + (q - tmp) * (r - t[j]);
        if (tmp > s[j]) ans -= tree.get_sum(tree.root, s[j] + 1, tmp);
    }
    printf("%lld\n", ans);
    return 0;
}
```
