题目描述:
有 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;
}