【题目】
题目描述:
给定笛卡尔坐标系上 n 个不重复的点。
定义一个 L 形为:
一个形如 (x,y),(x+1,y)...(x+a,y),(x,y+1)...(x,y+b) 的点集。
并且满足 a,b≥1 且 gcd(a,b)=1。
求有多少个集合的二元组 (A,B) 满足 A 和 B 都是 L 形,且 A 和 B 没有交,即A∩B=φ。其中 A 和 B 是两个集合,A 和 B可以相等。,
当 A≠B 时,我们将 (A,B) 和 (B,A) 视为不同的二元组。
输入格式:
第一行一个整数 n 。
接下来 n 行每行两个正整数 xi,yi 描述点的坐标。
输出格式:
输出一个整数,表示答案。
样例数据:
输入:
1 1
1 2
2 1
3 3
3 4
4 3
输出:
2
备注:
数据规模与约定:设坐标 xi,yi 的范围为 [1,S]。
对于 30% 的数据,S≤10。
对于 50% 的数据,S≤50。
对于 100% 的数据,S≤200,0≤n≤40000,n≤S2。
【分析】
对于这道题,直接求答案是不好求的
我们不妨换个角度,先算出所有的 L 形,再减去所有相交的就是答案
对于有交点,有以下几种情况:
(红线和黑线代表两个不同的 L 形)
具体的操作可以看代码
【代码】
(自认为代码还是写得简单易懂)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=40005;
const int s=200,S=205;
int n;
int a[S][S],g[S][S],p[S][S],up[S][S],right[S][S];
//g[i][j]表示的是有多少个L向右的边覆盖了格子(i,j)
//p[i][j]表示的是中心点向上有i个点,向右有j个点,这个时候L形的数量
int gcd(int x,int y)
{
int z=x%y;
while(z!=0)
{
x=y;
y=z;
z=x%y;
}
return y;
}
void init()
{
memset(a,0,sizeof(a));
memset(p,0,sizeof(p));
memset(up,0,sizeof(up));
memset(right,0,sizeof(right));
int i,j;
for(i=1;i<=s;++i)
{
for(j=1;j<=s;++j)
{//这里p[i][j]根据定义是等于p[i-1][j]+p[i][j-1]的,减去p[i-1][j-1]是因为它重复计算了两次
if(gcd(i,j)==1) p[i][j]=p[i-1][j]+p[i][j-1]-p[i-1][j-1]+1;
else p[i][j]=p[i-1][j]+p[i][j-1]-p[i-1][j-1];
}
}
}
long long get_L()
{
int i,j;
long long tot=0;
for(i=s;i>=1;--i) //因为L形是上方和右方,所以从后往前枚举
for(j=s;j>=1;--j)
if(a[i][j])
{
up[i][j]=up[i+1][j]+1; //这个L形往上的点的个数增加
right[i][j]=right[i][j+1]+1; //这个L形往右的点的个数增加
}
for(i=1;i<=s;++i)
for(j=1;j<=s;++j)
if(a[i][j])
{
up[i][j]--; //使两边(除中心点)上的up,right值为0,方便后面的操作
right[i][j]--;
}
for(i=1;i<=s;++i)
for(j=1;j<=s;++j)
if(a[i][j])
tot+=p[up[i][j]][right[i][j]]; //这个时候,两边上会有1个值为0,p数组中值为0,因此只会统计中心点
return tot*(tot-1); //顺序问题
}
long long remove1()
{
int i,j;
long long tot=0;
for(i=1;i<=s;++i)
for(j=1;j<=s;++j)
if(a[i][j])
tot+=p[up[i][j]][right[i][j]]*(p[up[i][j]][right[i][j]]-1);
return tot;
}
long long remove2()
{
int i,j,k;
long long ans,res,tot=0;
for(i=1;i<=s;++i)
for(j=1;j<=s;++j)
if(a[i][j])
for(k=i+1;a[k][j];++k) //枚举每个L形向右的点
g[k][j]+=p[up[i][j]][right[i][j]]-p[k-i-1][right[i][j]];
for(i=1;i<=s;++i)
for(j=1;j<=s;++j)
if(a[i][j])
{
for(k=j+1;a[i][k];++k)
{
ans=p[up[i][j]][right[i][j]]-p[up[i][j]][k-j-1];
tot+=2*ans*g[i][k];
}
}
for(i=1;i<=s;++i)
for(j=1;j<=s;++j)
if(a[i][j])
{
for(k=i+1;a[k][j]&&k<=up[i][j]+i;++k)
{
ans=p[up[i][j]][right[i][j]]-p[k-i-1][right[i][j]];
res=p[up[k][j]][right[k][j]];
tot+=2*ans*res;
}
for(k=j+1;a[i][k]&&k<=j+right[i][j];++k)
{
ans=p[up[i][j]][right[i][j]]-p[up[i][j]][k-j-1];
res=p[up[i][k]][right[i][k]];
tot+=2*ans*res;
}
}
return tot;
}
int main()
{
// freopen("draw.in","r",stdin);
// freopen("draw.out","w",stdout);
int x,y,i,j;
long long s1,s2,s3;
scanf("%d",&n);
init(); //初始化+预处理
for(i=1;i<=n;++i)
{
scanf("%d%d",&x,&y);
a[x][y]=1;
}
s1=get_L(); //处理L形并计算总个数
s2=remove1(); //第一种情况
s3=remove2(); //第二、三、四种情况
printf("%lld",s1-s2-s3);
// fclose(stdin);
// fclose(stdout);
return 0;
}