题解-富有物理组的风采

Problem

链接

简要题意:给定平面上\(n\)个点,求有多少种选取四个点的方案使得这四个点所构成的四边形为与坐标轴平行的矩形

\(n,x,y\leq 10^5\)

Solution

相信dalao的你一定能在一个极短的时间\(\Delta T\)内想到这道水题的做法

我们首先考虑暴力做法,首先找到一个平行于坐标轴的矩形可以枚举横坐标或者纵坐标,则有一个骗分策略就是如果不同的横坐标比较少,我们枚举横坐标,如果不同的纵坐标比较少,我们枚举纵坐标

细化一下做法,如果枚举横坐标,对于每一对横坐标\(x_1,x_2\),如果在相同的纵坐标处有都有点(即\((x_1,y),(x_2,y)\)都有点),我们将计数器\(cnt\)加一,最后这对横坐标的贡献就是\(\binom {cnt}2\)

如果枚举纵坐标,稍微转化一下,在枚举横坐标时进行统计,相应的,如果对于一个横坐标\(x\),在\((x,y_1),(x,y_2)\)处都有点,则对于数对\((y_1,y_2)\)进行计数,最后如果数对\((y_1,y_2)\)出现了\(cnt\)次,则对于答案的贡献为\(\binom {cnt}2\)

想到这就有眉目了,当然是套路分块啦

我们先设置一个参数\(L\),然后枚举出现过的横坐标\(x\),如果在这个横坐标上出现的点的个数超过\(L\),使用第一个做法,如果不足\(L\),使用第二个做法

现在来分析复杂度:对于第一个算法,调用不会超过\(\frac NL\)次,每次实现复杂度\(O(N)\),总复杂度\(O(\frac {N^2}L)\);对于第二个算法,调用不超过\(N\)次,每次实现复杂度不超过\(O(L)\),总复杂度为\(O(NL)\)

所以整个算法的复杂度为\(O(\frac {N^2}L+NL)\),运用不等式思想很快发现当\(L\)\(\sqrt N\)时最快,整体复杂度为\(O(N\sqrt N)\)

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rg register

template <typename _Tp> inline _Tp read(_Tp&x){
    char c11=getchar(),ob=0;x=0;
    while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')ob=1,c11=getchar();
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=101000,L=300;
struct Edge{int v,nxt;}a[N*80];
int X[N],Y[N],stx[N],cX[N],t[N];
int n,m,_;ll ans;

inline void add(int*arr,int u,int v){a[++_].v=v,a[_].nxt=arr[u],arr[u]=_;}

void init();
void work();
int main(){
    init();
    work();
    return 0;
}

void work(){
    int nn=unique(stx+1,stx+n+1)-stx-1;
    for(rg int id=1;id<=nn;++id)
        if(cX[stx[id]]>=L){
            for(rg int i=X[stx[id]];i;i=a[i].nxt)
                t[a[i].v]=1;
            for(rg int i=1;i<=nn;++i)
                if(i<id||cX[stx[i]]<L){
                    int res(0);
                    for(rg int j=X[stx[i]];j;j=a[j].nxt)
                        res+=t[a[j].v];
                    ans+=1ll*res*(res-1)>>1;
                }
            for(rg int i=X[stx[id]];i;i=a[i].nxt)
                t[a[i].v]=0;
        }else {
            for(rg int i=X[stx[id]];i;i=a[i].nxt)
            for(rg int j=a[i].nxt;j;j=a[j].nxt)
                if(a[i].v<a[j].v)add(Y,a[i].v,a[j].v);
                else add(Y,a[j].v,a[i].v);
        }
    for(rg int i=1;i<N;++i)if(Y[i]){
        for(rg int j=Y[i];j;j=a[j].nxt)
            ans+=(t[a[j].v]++);
        for(rg int j=Y[i];j;j=a[j].nxt)
            --t[a[j].v];
    }printf("%lld\n",ans);
}

void init(){
    read(n);
    for(rg int i=1,yy;i<=n;++i){
        ++cX[read(stx[i])];
        read(yy);add(X,stx[i],yy);
    }sort(stx+1,stx+n+1);
}

转载于:https://www.cnblogs.com/penth/p/9703380.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值