0710-模拟?-HDU5116

 

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;
}//此处循环枚举的是长度 
	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;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值