思路:
首先,判断极角相同可以用斜率表示。用一组最小正整数表示一个极角。
我们可以将没个极角标号,而对于一个点,我们可以看作它是把两个不同的极角连边。
那么问题就变成了:给定一张图,两条边可匹配当且仅当它们连接一个相同的点
,求每条边唯一匹配的最大匹配数量。 最大边匹配
这里有个结论:对于一个无向图,使用遍历同时给图中节点打上深度标记后,除去
走过的边,剩下的边连接的两个顶点它们的深度一定不相同。显然,如果存在某条边两端深度相同,那么在
遍历到其中一段时,一定会走这条边遍历到另一端。因而,这样没有被
遍历的边称为返祖边。
处理这一类问题可以建一颗dfs序树,从叶子节点开始考虑,先不考虑这个点连向父亲的边,如果连接其的边为偶数,那么就可以两两匹配掉,连向父亲的边还给父亲;如果不是,那么利用连向父亲的边来平衡这个奇偶性。可以保证匹配数最大,因为每个点(除了根节点)都有一条连向父亲的边平衡奇偶性,且必然没被用过,所以对于每个点(除了根节点),连接它的边都能够两两匹配不剩(除连接的父边)。
MyCode:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=4e5+7,mod=1e9+7;
vector<pair<int,int> >g[maxn],ans;
int n,ID,vis[maxn],dep[maxn];
map<pair<ll,ll>,int>mp;
int id(ll p,ll q) {
ll g=__gcd(p,q);
p/=g; q/=g;
if(mp.count({p,q})) return mp[{p,q}];
return mp[{p,q}]=++ID;
}
bool dfs(int v) {
vis[v]=1;
int f(0),whoup(0),u,who;
auto hit = [&](int x) {
if (!f) f=x;
else {
ans.emplace_back(make_pair(x,f));
f=0;
}
};
for(auto i:g[v]) {
u=i.first,who=i.second;
if(!vis[u]) {
dep[u]=dep[v]+1;
if(dfs(u)) hit(who);
}
else {
if(!whoup && dep[u] + 1 == dep[v]) whoup=who;
else if(dep[u]<dep[v]) hit(who);
}
}
if(f && whoup) {
hit(whoup); return false;
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(int i=1;i<=n;++i) {
ll a,b,c,d;
cin>>a>>b>>c>>d;//(a/b)横坐标 (c/d)纵坐标
int e1=id((c+d)*b,a*d);//向上 (c/d+1)的向量
int e2=id(c*b,(a+b)*d);//向右 (a/b+1)的向量
g[e1].emplace_back(make_pair(e2,i));
g[e2].emplace_back(make_pair(e1,i));
}
for(int i=1;i<=ID;++i) if(!vis[i])
dfs(i);
cout<<ans.size()<<'\n';
for(auto i:ans) cout<<i.first<<' '<<i.second<<'\n';
return 0;
}

京公网安备 11010502036488号