牛客:A.Laptop(二维偏序入门)

题目链接:https://ac.nowcoder.com/acm/contest/16/A

  • 刚刚看了二维偏序发现还是很简单的,二维偏序可以这样解释,现在平面上存在很多点,询问有多少个点能找到比它的点,(大可以解释为:两点 p o i n t 1 ( x 1 , y 1 ) point1(x1, y1) point1(x1,y1) p o i n t 2 ( x 2 , y 2 ) point2(x2, y2) point2(x2,y2) x 1 &lt; = x 2 x1&lt;=x2 x1<=x2 && y 1 &lt; = y 2 y1&lt;=y2 y1<=y2,这样则说 p o i n t 1 point1 point1小于 p o i n t 2 point2 point2)。
  • 如果暴力枚举去寻找复杂度是 n 2 n^{2} n2,但是仔细想想可以先将所有的点按照x升序排序,这样我们将排序后的点按照y值归并排序,这个时候如果两个区间要合并,在左方区间如果有一点 i i i,它的 y y y值小于右方区间某点 j j j y y y值,那么肯定可以说点 i i i小于 j j j(因为按照 x x x排序的时候左方区间所有点的 x x x值一定小于等于右方区间点 x x x值)。
  • 再仔细想想按照 x x x值升序排序之后可以从后面开始用树状数组维护 y y y值就可以用维护逆序对的方式直接解决了。


写了个归并树来模拟归并排序的过程

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;

struct Node {
    int a, b, pos;

    bool operator < (const Node &x) const {
        if(x.a != a) return a < x.a;
        else return b < x.b;
    }
}node[maxn];//记录每个点,pos是输入顺序

vector <Node> ve[maxn];//归并树
int n;
bool vis[maxn];//记录当前点是否能找到比它大的点

void init() {
    scanf("%d", &n);
    for(int i=1;i<=n;i++) {
        scanf("%d%d", &node[i].a, &node[i].b);
        node[i].pos = i;
    }
    sort(node+1, node+1+n);
}

void merge(int root) {//用归并树来模拟一个归并排序的过程
    int chl = root<<1;
    int chr = root<<1|1;

    int totl = 0, totr = 0;
    while(totl < ve[chl].size() || totr <ve[chr].size()) {
        if(totl < ve[chl].size() && totr == ve[chr].size()) {
            ve[root].push_back(ve[chl][totl++]);
        } else if(totr < ve[chr].size() && totl == ve[chl].size()) {
            ve[root].push_back(ve[chr][totr++]);
        } else {
            int val = ve[chl][totl].b;
            int var = ve[chr][totr].b;

            if(val <= var) {
                vis[ve[chl][totl].pos] = true;
                ve[root].push_back(ve[chl][totl++]);
            } else {
                ve[root].push_back(ve[chr][totr++]);
            }
        }
    }
}

void build_tree(int root, int l, int r) {
    if(l == r) {
        ve[root].push_back(node[l]);
        return ;
    }

    int mid = l + r >> 1;
    build_tree(root<<1, l, mid);
    build_tree(root<<1|1, mid+1, r);

    merge(root);
}

int main() {
//    freopen("1.in.txt", "r", stdin);
    init();
    build_tree(1, 1, n);//建立归并树

    int ans = 0;
    for(int i=1;i<=n;i++) {
        if(vis[node[i].pos]) ans++;
    }
    printf("%d\n", ans);
}


直接用树状数组维护,注意离散化一下

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;

struct Node {
    int a, b, pos;

    bool operator < (const Node &x) const {
        if(x.a != a) return a < x.a;
        else return b < x.b;
    }
}node[maxn];

int n, sum[maxn];
vector <int> ve;//用于离散化

void init() {
    scanf("%d", &n);
    for(int i=1;i<=n;i++) {
        scanf("%d%d", &node[i].a, &node[i].b);
        ve.push_back(node[i].a);
        ve.push_back(node[i].b);
        node[i].pos = i;
    }
    
    //离散化一下
    sort(ve.begin(), ve.end());
    ve.erase(unique(ve.begin(), ve.end()), ve.end());
    for(int i=1;i<=n;i++) {
        node[i].a = int(lower_bound(ve.begin(), ve.end(), node[i].a) - ve.begin() + 1);
        node[i].b = int(lower_bound(ve.begin(), ve.end(), node[i].b) - ve.begin() + 1);
    }
    
    sort(node+1, node+1+n);
}

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

void add(int x) {
    while(x <= ve.size()) {
        sum[x]++;
        x += lowbit(x);
    }
}

int get_sum(int x) {
    int Sum = 0;
    if(x <= 0 ) return 0;
    while(x > 0) {
        Sum += sum[x];
        x -= lowbit(x);
    }
    return Sum;
}

int main() {
//    freopen("1.in.txt", "r", stdin);
    init();
    int ans = 0;
    for(int i=n;i>0;i--) {
        int temp = get_sum(ve.size()) - get_sum(node[i].b);
        if(temp > 0) ans++;
        add(node[i].b);
    }
    printf("%d\n", ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值