思路:
首先,判断极角相同可以用斜率表示。用一组最小正整数表示一个极角。
我们可以将没个极角标号,而对于一个点,我们可以看作它是把两个不同的极角连边。
那么问题就变成了:给定一张图,两条边可匹配当且仅当它们连接一个相同的点,求每条边唯一匹配的最大匹配数量。 最大边匹配
这里有个结论:对于一个无向图,使用遍历同时给图中节点打上深度标记后,除去走过的边,剩下的边连接的两个顶点它们的深度一定不相同。显然,如果存在某条边两端深度相同,那么在遍历到其中一段时,一定会走这条边遍历到另一端。因而,这样没有被遍历的边称为返祖边。
处理这一类问题可以建一颗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; }