牛客网暑期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;
}

猜你喜欢

转载自blog.csdn.net/weixin_39453270/article/details/81392111