Description
一个点集SSS是合法的当且仅当对该集合的每个子集TTT,都存在三元组(a,l,r)(a,l,r)(a,l,r),使得h(a,l,r)∩S=Th(a,l,r)\cap S=Th(a,l,r)∩S=T,其中h(a,l,r)={(x,y)∣x≥a,l≤y≤r}h(a,l,r)=\{(x,y)|x\ge a,l\le y\le r\}h(a,l,r)={(x,y)∣x≥a,l≤y≤r}
给出nnn个点组成的点集,找出其合法非空子集个数
Input
第一行一整数nnn表示点数,之后nnn行每行输入一个点的横纵坐标x,yx,yx,y
(1≤n≤105,1≤x,y≤109)(1\le n\le 10^5,1\le x,y\le 10^9)(1≤n≤105,1≤x,y≤109)
Output
输出所给点集的合法子集个数,结果模998244353998244353998244353
Sample Input
3
1 1
2 2
3 3
Sample Output
6
Solution
1.单点集必然可以,方案数nnn
2.两个点的点集,若两点纵坐标相同则不行,其他情况都可以,统计纵坐标为iii出现的次数numinum_inumi,那么方案数为Cn2−∑Cnumi2C_n^2-\sum C_{num_i}^2Cn2−∑Cnumi2
3.三个点的点集,三点纵坐标均不能相同,且有一点横坐标需要严格小于另外两点横坐标,假设三点坐标为(x1,y1),(x2,y2),(x3,y3)(x_1,y_1),(x_2,y_2),(x_3,y_3)(x1,y1),(x2,y2),(x3,y3),那么有x1<x2,x3,y2<y1<y3x_1<x_2,x_3,y_2<y_1<y_3x1<x2,x3,y2<y1<y3,考虑(x1,y1)(x_1,y_1)(x1,y1)对答案的贡献,即为所有横坐标大于x1x_1x1,纵坐标大于y1y_1y1的点的个数乘上横坐标大于x1x_1x1,纵坐标小于y1y_1y1的点的个数,将所有点按横坐标从大到小排序,将纵坐标插入树状数组中计数即可
4.超过三个点的点集无解
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
#define maxn 100005
struct BIT
{
#define lowbit(x) (x&(-x))
int b[maxn],n;
void init(int _n)
{
n=_n;
for(int i=1;i<=n;i++)b[i]=0;
}
void update(int x,int v)
{
while(x<=n)
{
b[x]+=v;
x+=lowbit(x);
}
}
int query(int x)
{
int ans=0;
while(x)
{
ans+=b[x];
x-=lowbit(x);
}
return ans;
}
}bit;
P a[maxn];
int n,h[maxn],num[maxn];
#define x first
#define y second
#define mod 998244353
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
h[i-1]=a[i].y;
}
sort(h,h+n);
int m=unique(h,h+n)-h;
for(int i=1;i<=n;i++)
{
a[i].y=lower_bound(h,h+m,a[i].y)-h+1;
num[a[i].y]++;
}
bit.init(m);
sort(a+1,a+n+1);
ll ans=n+1ll*n*(n-1)/2;
for(int i=1;i<=m;i++)ans-=1ll*num[i]*(num[i]-1)/2;
for(int i=n;i>=1;i--)
{
int j=i;
while(j>=1&&a[j].x==a[i].x)j--;
j++;
for(int k=j;k<=i;k++)
ans+=1ll*(bit.query(m)-bit.query(a[k].y))*bit.query(a[k].y-1);
for(int k=j;k<=i;k++)bit.update(a[k].y,1);
i=j;
}
printf("%lld\n",ans%mod);
return 0;
}