Draw
题目描述
给定笛卡尔坐标系上 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
输入
6
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≤4*104,n≤S2。
题解
其实上一篇博客都有提到过,我们如果直接求答案不好求,那就求出包含答案的一个东东,再减去不满足条件的,就得到答案啦
这道题也是这样的,我们发现求出所有的L形并不是一件难事,(只需要预处理出每一个点往右有多少,往上有多少,再弄个gcd,然后就O(n)的复杂度找出来啦),如果还能求出相交的L形,则答案呼之欲出。现在就来考虑相交的情况(考场上多画一画,如下图),然后处理一下下就ok了。代码不是很好写,也有很多细节问题,我们具体到代码上去看看
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int S=200;
int u[S+10][S+10],rig[S+10][S+10],res[S+10][S+10];
int fix[S+10][S+10];
bool map[S+10][S+10];
int n;
inline int read(){
char ch;
while((ch=getchar())<'0'||ch>'9');
int ress=0;
while(ch>='0'&&ch<='9'){
ress=ress*10+ch-'0';
ch=getchar();
}
return ress;
}
int gcd(int x,int y){
int z=x%y;
while(z!=0){
x=y;
y=z;
z=x%y;
}
return y;
}
int main(){
n=read();
//printf("%d\n",n);
memset(map,0,sizeof(map));
int i,j,k;
for(i=1;i<=n;++i)
map[read()][read()]=1;
for(i=1;i<=S;++i)
for(j=1;j<=S;++j)
{
if(gcd(i,j)) res[i][j]=1;
else res[i][j]=0;
}//此处循环枚举的是长度
for(i=S;i>=1;--i)
for(j=S;j>=1;--j){
if(map[i][j]){
u[i][j]=u[i][j+1]+1;
rig[i][j]=rig[i+1][j]+1;
}
}
for(i=1;i<=S;++i)
for(j=1;j<=S;++j){
if(map[i][j]){
u[i][j]--;
rig[i][j]--;
}
} //预处理往右,往上的个数(也就是题目中所描述的a,b)
long long tot=0;
for(i=1;i<=S;++i)
for(j=1;j<=S;++j){
res[i][j]+=res[i][j-1]+res[i-1][j]-res[i-1][j-1];
}
for(i=1;i<=S;++i)
for(j=1;j<=S;++j){
if(map[i][j])
tot+=res[rig[i][j]][u[i][j]];
}//处理总共有多少个L形
long long res1=tot*(tot-1);//有顺序
long long res2=0;
for(i=1;i<=S;++i)
for(j=1;j<=S;++j)
{
if(map[i][j]){
res2+=res[rig[i][j]][u[i][j]]*(res[rig[i][j]][u[i][j]]-1);
}
} //res2-->保存图片中第一种情况
long long res3=0;
for(i=1;i<=S;++i)
for(j=1;j<=S;++j)
{
if(!map[i][j]) continue;
for(int l=i+1;map[l][j];++l){
fix[l][j]+=res[rig[i][j]][u[i][j]]-res[l-i-1][u[i][j]];
}
}//fix[i][j]-->保存图片中第2种情况,向右覆盖该交点的个数
for(i=1;i<=S;++i)
for(j=1;j<=S;++j){
if(!map[i][j]) continue;
for(int l=j+1;map[i][l];++l){
long long myfix=res[rig[i][j]][u[i][j]]-res[rig[i][j]][l-j-1];
// myfix-->保存图片中第2种情况,向上覆盖该交点的个数
res3+=2*myfix*fix[i][l];
}
}
for(i=1;i<=S;++i)
for(j=1;j<=S;++j)
{
if(!map[i][j]) continue;
for(int l=i+1;map[l][j]&&l<=rig[i][j]+i;++l){
long long myfix=res[rig[i][j]][u[i][j]]-res[l-i-1][u[i][j]];
long long myfix2=res[rig[l][j]][u[l][j]];
res3+=2*myfix*myfix2;
}
for(int l=j+1;map[i][l]&&l<=j+u[i][j];++l){
long long myfix=res[rig[i][j]][u[i][j]]-res[rig[i][j]][l-j-1];
long long myfix2=res[rig[i][l]][u[i][l]];
res3+=2*myfix*myfix2;
}
}
printf("%lld",res1-res2-res3);
return 0;
}