zoukankan      html  css  js  c++  java
  • 牛客网暑期ACM多校训练营(第五场)I(树状数组)

    题目描述:

        有 n 个点,一个点集 S 是好的,当且仅当对于他的每个子集 T,存在一个右边无限长的矩形,使得这个矩形包含了 T,但是和 S-T 没有交
        求这 n 个点里有几个好的点集
    1<=n<=10^5

    题目分析:

        首先要小小吐槽一下这个题目的题面。三个人三双眼睛看这个题都是愣是一直都看不明白题意。

        而当我们明白题目在说些什么之后,其实这个题目就可以去做了。

        我们要求总的方案数,那么我们可以分为多种情况进行描述

        1):当只选取1个点时,我们可以发现,任何一个点都满足题意。

        2):当我们选取2个点时,我们可以发现如果要满足一个无限向右的矩形只框住一个点,当且仅当两个点的纵坐标不相同。因此,对于选2个点的总的方案数等于C(n,2)-C(纵坐标相同的个数,2)

        3):当我们选3个点的时候(假设三个点为a,b,c),我们可以发现,当我们选取{a,b}作为子集,倘如第三个点c在{a,b}的右边,则我们发现由{a,b}组成的矩形一定包含{c},故不成立。因此{c}必定在{a,b}的左边,即当且仅当三个点的能够构成一个'<'号的形式才能够符合题意。

        4):当选4个点及以上时,我们发现不管怎么样摆,均不可能出现3)的情况,故4个点以上的点是不合理的。

        因此现在我们只需要处理的就是3)中的情况。对于3)的情况。我们只要求出在第i个点之前,有多少个点的x坐标比当前点大(记位below),再求出在第i个点之后有多少个点的x坐标比当前点大(记位above),那么对于第i个点而言,该点的方案数即为below*above了。

        而对于below和above值的维护,我们需要先将y坐标进行离散化,然后将数组按照x坐标进行排序,然后用树状数组对区间进行维护即可。

    代码:

    #include <bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    struct node{
        int x,y;
        bool operator<(const node &b)const{
            return x>b.x;
        }
    }q[maxn];
    int hav[maxn],san[maxn],bit[maxn];
    int lowbit(int x){
        return x&-x;
    }
    void update(int x,int num){
        while(x<maxn){
            bit[x]+=num;
            x+=lowbit(x);
        }
    }
    int query(int x){
        int res=0;
        while(x){
            res=(res+bit[x])%mod;
            x-=lowbit(x);
        }
        return res;
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&q[i].x,&q[i].y);
            san[i]=q[i].y;
        }
        //sort(q+1,q+1+n);
        sort(san+1,san+1+n);//离散化
        int tot=unique(san+1,san+1+n)-san-1;
        for(int i=1;i<=n;i++){
            q[i].y=lower_bound(san+1,san+1+tot,q[i].y)-san;
            hav[q[i].y]++;
        }
        ll ans=0;
        ans=(0ll+n+1ll*n*(n-1)/2)%mod;//求出第一种和第二种的情况的方案数
        for(int i=1;i<=tot;i++){//减去纵坐标相同的情况
            ans=(ans-1ll*hav[i]*(hav[i]-1)/2+mod)%mod;
        }
        int pre=1;
        sort(q+1,q+1+n);
        for(int i=1;i<=n;i++){
            int above=query(q[i].y-1);
            int below=query(tot)-query(q[i].y);
            ans=(ans+1ll*above*below%mod)%mod;
            if(q[i].x!=q[i+1].x){
                while(pre<=i){//树状数组更新
                    update(q[pre].y,1),pre++;
                }
            }
        }
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    Android——4.2
    【图像分割】网络最大流
    【OpenCV】内存溢出
    【xml】利用OpenCV解析
    【文件】读取一个文件夹下所有的jpg图片
    【QT】ui转代码
    【CCL】连通区域提取
    【Qt】学习笔记(一)
    【数据结构】Huffman树
    【数据结构】中序遍历线索二叉树
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007248.html
Copyright © 2011-2022 走看看