zoukankan      html  css  js  c++  java
  • Codeforces

    https://codeforces.com/contest/1191/problem/F
    看了一下题解的思路,感觉除了最后一段以外没什么启发。

    首先离散化x加快速度,免得搞多一个log。其实y不需要离散化。
    规定无穷大就是xn+1这个很好理解嘿嘿。(反正开多了5个不怕)

    注意到其实从上往下一行一行扫过去,每次必须新增的元素才是新的集合,那很容易想到一个不重不漏的办法就是每次计算“以点p[i]为加进去的新点中的结束的集合”,那么假设一开始p[i]的左侧有cntl个点,那么显然有(cntl+1)条线在p[i]的左侧,而p[i]的右侧有cntr个点,也是(cntr+1)条线。

    这个cntl显然就是query(1,p[i].x-1),而右侧则是query(p[i].x+1,p[i+1].x-1),因为不能包含同y的下一个点p[i+1],而其中,上面的点选法也会产生区别。

    那么每层加入一个正无穷也就是xn+1就可以了。

    溢出这种现在我不会错的了。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int n;
    struct Point {
        int x, y;
        bool operator<(const Point &p)const {
            return y == p.y ? x<p.x: y>p.y;
            //y从上到下,x从左到右
        }
    } p[200005];
    
    int x[200005];
    int y[200005];
    
    ll sum;
    
    const int MAXM = 200000;
    int st[(MAXM << 2) + 5];
    
    inline void push_up(int o) {
        st[o] = st[o << 1] + st[o << 1 | 1];
    }
    
    void build(int o, int l, int r) {
        if(l == r) {
            st[o] = 0;
        } else {
            int m = (l + r) >> 1;
            build(o << 1, l, m);
            build(o << 1 | 1, m + 1, r);
            push_up(o);
        }
    }
    
    void update(int o, int l, int r, int x, int v) {
        if(l == r) {
            //不是加,是赋值,同x的点是没有差别的
            st[o] = v;
            return;
        } else {
            int m = (l + r) >> 1;
            if(x <= m)
                update(o << 1, l, m, x, v);
            else if(x >= m + 1)
                update(o << 1 | 1, m + 1, r, x, v);
            push_up(o);
        }
    }
    
    int query(int o, int l, int r, int a, int b) {
        if(b < a)
            return 0;
        else if(a <= l && r <= b) {
            return st[o];
        } else {
            int m = (l + r) >> 1;
            int ans = 0;
            if(a <= m)
                ans = query(o << 1, l, m, a, b);
            if(b >= m + 1)
                ans += query(o << 1 | 1, m + 1, r, a, b);
            return ans;
        }
    }
    
    int vx[200005], vxtop;
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
        //freopen("Yinku.out", "w", stdout);
    #endif // Yinku
        while(~scanf("%d", &n)) {
            for(int i = 1; i <= n; i++) {
                scanf("%d%d", &p[i].x, &p[i].y);
                x[i] = p[i].x;
                y[i] = p[i].y;
            }
            sort(x + 1, x + 1 + n);
            int xn = unique(x + 1, x + 1 + n) - (x + 1);
            sort(y + 1, y + 1 + n);
            int yn = unique(y + 1, y + 1 + n) - (y + 1);
            for(int i = 1; i <= n; i++) {
                p[i].x = lower_bound(x + 1, x + 1 + xn, p[i].x) - x;
                p[i].y = lower_bound(y + 1, y + 1 + yn, p[i].y) - y;
                //从1开始分配新的坐标
                //printf("(%d,%d)
    ", p[i].x, p[i].y);
            }
            sort(p + 1, p + 1 + n);
            //扫描线
            sum = 0;
            build(1, 1, xn + 1);
            int beg = 1, cur = 1;
            while(beg <= n) {
                vxtop = 0;
                while(p[cur].y == p[beg].y) {
                    update(1, 1, xn + 1, p[cur].x, 1);
                    vx[++vxtop] = p[cur].x;
                    /*
                    //点是不会重合的,那包含这个最左侧的点的都是全新集合
                    int cntl = query(1, 1, xn, 1, p[cur].x - 1);
                    //在这个点的左侧有cntl个x不同的点,那就有cntl+1个位置
                    //sum += (cntl + 1); X
                    //是以这个点为右侧边界的,所以右侧没得选 X
                    */
                    //该层y中是以这个x点为右侧边界,但是两个x点之间的上层y也是可选的
                    cur++;
                }
                vx[++vxtop] = xn + 1;
                for(int i = 1; i <= vxtop - 1; i++) {
                    //该层最右端的新点为vx[i]的数量
                    int cntl = query(1, 1, xn + 1, 1, vx[i] - 1);
                    int cntr = query(1, 1, xn + 1, vx[i] + 1, vx[i + 1] - 1);
                    sum += 1ll * (cntl + 1) * (cntr + 1);
                }
                beg = cur;
            }
            printf("%lld
    ", sum);
        }
    }
    

    偶尔用下树状数组,把一些多余操作去掉之后的最快的做法。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int n;
    struct Point {
        int x, y;
        bool operator<(const Point &p)const {
            return y == p.y ? x<p.x: y>p.y;
            //y从上到下,x从左到右
        }
    } p[200005];
    
    int x[200005];
    
    ll _sum;
    
    const int MAXM = 200005;
    bool cntx[MAXM + 5];
    int bit[MAXM + 5];
    
    int upper;
    
    inline int sum(int x) {
        int res = 0;
        while(x) {
            res = res + bit[x];
            x -= x & -x;
        }
        return res;
    }
    
    inline void update(int x) {
        if(cntx[x])
            return;
        else {
            cntx[x] = true;
            while(x <= upper) {
                bit[x] += 1;
                x += x & -x;
            }
        }
    }
    
    inline int range_sum(int x, int y) {
        return sum(y) - sum(x - 1);
    }
    
    int *vx=x,vxtop;
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
        //freopen("Yinku.out", "w", stdout);
    #endif // Yinku
        while(~scanf("%d", &n)) {
            memset(cntx, false, sizeof(cntx));
            for(int i = 1; i <= n; i++) {
                scanf("%d%d", &p[i].x, &p[i].y);
                x[i] = p[i].x;
            }
            sort(x + 1, x + 1 + n);
            int xn = unique(x + 1, x + 1 + n) - (x + 1);
            for(int i = 1; i <= n; i++) {
                p[i].x = lower_bound(x + 1, x + 1 + xn, p[i].x) - x;
                //从1开始分配新的坐标
                //printf("(%d,%d)
    ", p[i].x, p[i].y);
            }
            sort(p + 1, p + 1 + n);
            //扫描线
            _sum = 0;
            upper = xn + 1;
            int beg = 1, cur = 1;
            while(beg <= n) {
                vxtop = 0;
                while(p[cur].y == p[beg].y) {
                    update(p[cur].x);
                    vx[++vxtop] = p[cur].x;
                    //该层y中是以这个x点为右侧边界,但是两个x点之间的上层y也是可选的
                    cur++;
                }
                vx[++vxtop] = xn + 1;
                for(int i = 1; i <= vxtop - 1; i++) {
                    //该层最右端的新点为vx[i]的数量
                    int cntl = range_sum(1, vx[i] - 1);
                    int cntr = range_sum(vx[i] + 1, vx[i + 1] - 1);
                    _sum += 1ll * (cntl + 1) * (cntr + 1);
                }
                beg = cur;
            }
            printf("%lld
    ", _sum);
        }
    }
    
  • 相关阅读:
    SQL表连接查询inner join left join right join full join ...
    希尔伯特空间回顾(简直太好了)
    pytorch的matmul怎么广播
    pytorch怎么抽取中间的特征或者梯度
    winedt102安装
    数据挖掘 关联规则
    git 命令
    caffe: c++11支持
    匈牙利算法
    ubuntu两个conda安装和切换
  • 原文地址:https://www.cnblogs.com/Yinku/p/11182211.html
Copyright © 2011-2022 走看看