C - Line-line Intersection

SDUT 2022 Spring Team Contest(for 21) - 14 - Virtual Judge

There are nn lines l_1,l_2,\dots,l_nl1​,l2​,…,ln​ on the 2D-plane.

Staring at these lines, Calabash is wondering how many pairs of (i,j)(i,j) that 1\leq i<j\leq n1≤i<j≤n and l_i,l_jli​,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\leq T\leq 1000)T(1≤T≤1000), denoting the number of test cases.

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

For the next nn lines, each line contains four integers xa_i,ya_i,xb_i,yb_i(|xa_i|,|ya_i|,|xb_i|,|yb_i|\leq 10^9)xai​,yai​,xbi​,ybi​(∣xai​∣,∣yai​∣,∣xbi​∣,∣ybi​∣≤109). It means l_ili​ passes both (xa_i,ya_i)(xai​,yai​) and (xb_i,yb_i)(xbi​,ybi​). (xa_i,ya_i)(xai​,yai​) will never be coincided with (xb_i,yb_i)(xbi​,ybi​).

It is guaranteed that \sum n\leq 10^6∑n≤106.

Output

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

Sample 1

InputcopyOutputcopy
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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double maxx=2e9+10;
map<double,ll>mp;
map<int,double>mk;
map<int,double>mb;
map<pair<double,double>,ll>mp1;
ll mul(ll n)
{
    return n*(n-1)/2;
}
int main()
{
    int t;
    scanf("%lld",&t);
    while(t--)
    {
        mp.clear();
        mk.clear();
        mb.clear();
        mp1.clear();
        ll n;
        scanf("%lld",&n);
        for(int i=0;i<n;i++)
        {
            double x1,x2,y1,y2;
            double k,b;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            if(x1==x2)
            {
                k=maxx;
                b=x1;
            }
            else
            {
                k=1.0*(y1-y2)/(x1-x2);
                b=y1-k*x1;
            }
            mp[k]++;
            mk[i]=k;
            mb[i]=b;
            mp1[make_pair(k,b)]++;
        }
        ll sum=0;
        for(int i=0;i<n;i++)
        {
            if(mp[mk[i]]>1)
            {
                sum+=mul(mp[mk[i]]);
                mp[mk[i]]=0;//遍历时,如果不清零,后面又会取到mk[i],对结果有影响
            }
        }
        for(int i=0;i<n;i++)
        {
            if (mp1[make_pair(mk[i], mb[i])] > 1)
            {
                sum -= mul(mp1[make_pair(mk[i], mb[i])]);
                mp1[make_pair(mk[i], mb[i])] = 0;//同上
            }
        }
        ll ans = mul(n) - sum ;
        cout << ans << endl;

    }
}

 

思路:

题目给的数据都不小,每个坐标的绝对值大小都<=1e9,所以我们要用map来模拟一下。中学生都知道的知识,两条直线,想要有交点,就必须斜率不同。另外,题目说明两条相同的直线,也算是有交点。所以我们在用map记住斜率的时候一定要特别注意这种情况,我们知道一条直线的一般方程是y=kx+b,所以,判断两条有相同斜率的直线是不是同一条,就可以看一下b=y-kx的值,要求输入直线上的坐标,所以b不难求。
 

k = 1.0 * (y1 - y2) / (x1 - x2);
b = y1 - k * x1;

注意注意注意,我们在中学做一些关于直线的题目的时候,都会想到一种特殊的直线,就是垂直x轴的直线,这样的直线x1==x2,所以k的大小就没有办法用这个公式来求。当时我做的时候也是忘了这一茬,所以就WA了一两次。我再一看题目,每个坐标的绝对值大小都<=1e9,而且都是整数,所以在不考虑上述情况下,|k|<=(1e9+1e9)/1,我看索性把斜率不存在的情况中的k记为2e9+1,b就记为x1。然后这种情况就可以和正常的情况一起做了。然后,我们知道如果有n条斜率不同的线,他们的交点有(n-1) + (n-2) + ... + 2 + 1 = n * (n - 1) / 2 个。先写个函数:
 

ll mul(ll n)
{
    return n * (n - 1) / 2;
}

我们用mp[k]++来记住有多少条直线的斜率为k,再用mp1[make_pair(k, b)]++来记住斜率相同的时候会不会有同一条直线的情况。假设每条直线的斜率都不同ans = mul(n),然后减去k相同的直线不能形成的交点(平行),当然也必须是mul(n1),还要加上k相同b相同的情况mul(n2)。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值