牛客网暑期ACM多校训练营(第五场) I--vcd

题目描述

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

思路

当时这题是我队友看的,然后看完题后和我们说,但听不懂,随后他直接想了一个点和两个点的情况,没有想到会有三个点的情况,交了题WA了,然后他怀疑自己读错题目了,然后我们就放弃了。赛后才知道原来没有读错题目,只是漏了一种情况。

官方题解:

对于 |S|=1,他显然是好的
对于 |S|=2,只要两个点的 y 坐标不相同,那么这个集合也是好的
对于 |S|=3,三个点的形状必须是 < 型
对于 |S|>3,不可能任何三个点都是 < 型,所以一定不是好的
用树状数组统计一下就好了
时间复杂度:O(nlogn)

代码如下:

#include 
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int mod=998244353;
int tree[400005];
ll n;
int b[100005];
ll box[100005];
struct node
{
    int x,y;
    bool friend operator <(node w,node e)
    {
        return w.x>e.x;
    }
}a[100005];
void add(int x,int c){
    while(x<=n){
        tree[x]+=c;
        x += x&(-x);
    }
}
int sum(int x){
    int res = 0;
    while(x){
        res += tree[x];
        x -=x&(-x);
    }
    return res;
}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        b[i]=a[i].y;
    }
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++)
    {
        a[i].y=lower_bound(b+1,b+1+n,a[i].y)-b;
        box[a[i].y]++;
    }
    ll ans=n+n*(n-1)/2;//统计一个点和两个点的情况
    for(int i=1;i<=n;i++)
    ans-=box[i]*(box[i]-1)/2;//减去两个点有相同的y的情况
    ans=(ans+mod)%mod;
    int pre=1;
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
    {
        ll down=sum(a[i].y-1);
        ll up=sum(n)-sum(a[i].y);
        ans=(ans+(down*up))%mod;
        if(a[i].x!=a[i+1].x){
            for(;pre<=i;pre++)
            add(a[pre].y,1);
        }
    }
    printf("%lld\n",ans);
	return 0;
}

 

你可能感兴趣的:(树状数组)