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);
        }
    }
    
  • 相关阅读:
    在Ubuntu中通过update-alternatives切换软件版本
    SCons: 替代 make 和 makefile 及 javac 的极好用的c、c++、java 构建工具
    mongodb 的使用
    利用grub从ubuntu找回windows启动项
    How to Repair GRUB2 When Ubuntu Won’t Boot
    Redis vs Mongo vs mysql
    java script 的工具
    python 的弹框
    how to use greendao in android studio
    python yield的终极解释
  • 原文地址:https://www.cnblogs.com/Yinku/p/11182211.html
Copyright © 2011-2022 走看看