题解: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;
}

浙公网安备 33010602011771号