题解:abc402-D- Line Crossing

题目传送门

这题的题目大意是:有 \(n\) 个点平均分布在一个单位圆上,编号顺时针分别是 \(1,2,3...n\)。现在给定 \(m\)\(a_i\)\(b_i\),在两点之间连一条直线。问有多少条直线相交。

形如以上图片,即 \(n=8,m=3\)\(a_i=1,1,2\)\(b_i=8,5,4\),则第一条直线与第二条直线相交,第一条直线还与第三条直线相交。所以答案为三。

容易想到容斥,先求出所有可能相交的情况的总和,即为 \(\frac{n\times (n-1)}{2}\),再减去非法情况,即平行的直线二元组的数量,设平行的直线的数量为 \(cnt\),那么二元组数量即为 \(\frac{cnt\times (cnt-1)}{2}\)。又因为可能在多个方向上平行,所以每个方向上的平行直线数量要分开算。

观察即可发现,如果一条直线用 \((i,j)\) 来表示,\(i,j\) 是点的编号,那么它的平行直线可以用 \(((i+k)\bmod n,(j-k+n)\bmod n)\) 来表示。化简后发现,任意两条平行直线的 \((i+j)\bmod n\) 相等,那么就可以通过这个性质快速统计出平行直线的数量,再根据上文推得结论即可算出答案。

代码如下。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3010101;
string s;
ll ans,n,m,k[N];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
    	ll x,y;
		cin>>x>>y;
		k[(x+y)%n]++;
	}
	ans=(m-1)*m/2;
	for(int i=0;i<=n;i++)ans-=(k[i]-1)*k[i]/2;
	cout<<ans;
    return 0;
}
posted @ 2025-09-10 21:39  一班的hoko  阅读(3)  评论(0)    收藏  举报