The 13th Chinese Northeast Collegiate Programming Contest (2019东北赛)

博客围绕二维平面上直线交点对数问题展开。给定n条直线,需找出满足条件的直线对数量。介绍了输入输出格式及测试用例情况,还给出将直线写成一般表达式判断平行、重合的方法,以及计算交点对数的公式,并展示了两种不同的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

C. Line-line Intersection

time limit per test

6.0 s

memory limit per test

512 MB

input

standard input

output

standard output

There are nn lines l1,l2,…,lnl1,l2,…,ln on the 2D-plane.

Staring at these lines, Calabash is wondering how many pairs of (i,j)(i,j) that 1≤i<j≤n1≤i<j≤n and li,ljli,lj share at least one common point. Note that two overlapping lines also share common points.

Please write a program to solve Calabash's problem.

Input

The first line of the input contains an integer T(1≤T≤1000)T(1≤T≤1000), denoting the number of test cases.

In each test case, there is one integer n(1≤n≤100000)n(1≤n≤100000) in the first line, denoting the number of lines.

For the next nn lines, each line contains four integers xai,yai,xbi,ybi(|xai|,|yai|,|xbi|,|ybi|≤109)xai,yai,xbi,ybi(|xai|,|yai|,|xbi|,|ybi|≤109). It means lili passes both (xai,yai)(xai,yai) and (xbi,ybi)(xbi,ybi). (xai,yai)(xai,yai) will never be coincided with (xbi,ybi)(xbi,ybi).

It is guaranteed that ∑n≤106∑n≤106.

Output

For each test case, print a single line containing an integer, denoting the answer.

Example

input

Copy

3
2
0 0 1 1
0 1 1 0
2
0 0 0 1
1 0 1 1
2
0 0 1 1
0 0 1 1

output

Copy

1
0
1

将直线写成一般表达式(ax+by+c=0),对于两条直线,若a和b相同,则平行或重合,若abc相同,则一定重合

ans=总方案数-平行或重合的方案数+重合的方案数

代码一:比赛中的写法(有些麻烦)

#include <bits/stdc++.h>
using namespace std;
using ll=long long ;
using ull=unsigned long long;
const int maxn=1e5+5;
struct line{ll ax,ay,bx,by;}L[maxn];
struct node{ll a,b,c;}A[maxn];
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
int main()
{
	int t;scanf("%d",&t);
	while(t--)
	{
		ll n;scanf("%lld",&n);ll ans1=0;ll ans2=0;
		for(int i=1;i<=n;i++){scanf("%lld%lld%lld%lld",&L[i].ax,&L[i].ay,&L[i].bx,&L[i].by);}
		for(int i=1;i<=n;i++)
		{
			A[i].a=L[i].by-L[i].ay;A[i].b=L[i].ax-L[i].bx;
			A[i].c=L[i].bx*L[i].ay-L[i].ax*L[i].by;
			ll tmp=gcd(A[i].a,gcd(A[i].b,A[i].c));
			A[i].a/=tmp;A[i].b/=tmp;A[i].c/=tmp;
		}
		unordered_map<ull,ll>mp1;
		for(int i=1;i<=n;i++)
		{
			mp1[A[i].a * 1000000007LL * 1000000009LL +A[i].b * 998244353LL + A[i].c]++;
		}
        for(auto it:mp1)ans1+=1LL*(it.second*(it.second-1)/2);
		for(int i=1;i<=n;i++)
		{
			A[i].a=L[i].by-L[i].ay;A[i].b=L[i].ax-L[i].bx;
			A[i].c=0;
			ll tmp=gcd(A[i].a,A[i].b);
			A[i].a/=tmp;A[i].b/=tmp;
		}
		unordered_map<ull,ll>mp2;
		for(int i=1;i<=n;i++)
		{
			mp2[A[i].a * 1000000007LL * 1000000009LL +A[i].b * 998244353LL ]++;
		}
		for(auto it:mp2)ans2+=1LL*(it.second*(it.second-1)/2);
		printf("%lld\n",((n*(n-1)/2-ans2+ans1)));
	}
	return 0;
}

代码二(更简洁,思路都一样):

#include <bits/stdc++.h>
using namespace std;
using ll=long long ;
using ull=unsigned long long;
map<pair<ll,ll>,ll >mp1;
map<tuple<ll,ll,ll>,ll >mp2;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
int main()
{
	int t;scanf("%d",&t);
	while(t--)
	{
		ll n;scanf("%lld",&n);mp1.clear();mp2.clear();
		for(int i=1;i<=n;i++)
		{
			ll a,b,c,d;scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
			ll A=d-b;ll B=a-c;ll C=b*c-a*d;ll g1=gcd(A,B);ll g2=gcd(A,gcd(B,C));
			mp1[make_pair(A/g1,B/g1)]++;mp2[make_tuple(A/g2,B/g2,C/g2)]++;
		}
		ll ans1=0,ans2=0;// ans1 重合或平行  //ans2 重合
		for(auto it:mp1)ans1+=it.second*(it.second-1)/2;
		for(auto it:mp2)ans2+=it.second*(it.second-1)/2;
		printf("%lld\n",n*(n-1)/2-ans1+ans2);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值