Newcoder 143 I.vcd(BIT)

探讨了如何计算一个由 n 个点组成的点集中,合法非空子集的数量,并提供了一种有效的算法实现,包括单点、双点及三点情况下的合法子集计数方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)xa,lyr}

给出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)(1n105,1x,y109)

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}^2Cn2Cnumi2

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&lt;x2,x3,y2&lt;y1&lt;y3x_1&lt;x_2,x_3,y_2&lt;y_1&lt;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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值