1, General idea of the topic
Give you n points. Each point can and must be displaced once (move one unit horizontally to the right or move one unit vertically upward). Ask how many pairs of points can be matched after displacement (as long as two points are collinear with the origin, three points can be matched). Each point can only be matched once. Ask the maximum matching number and matching scheme.
Example 1 also emphasizes that each point can only be matched once, and there may be coincident points, and each point must also be moved.
2, Algorithm analysis
① tan value is used to measure whether it is collinear. According to the principle of avoiding decimal as much as possible, tan value can be characterized by fractional structure.
② Turn a point into an edge. If a point has two translation modes, you can use an oblique edge to replace a point in the original figure, as shown in the figure
For beveled edges, the two endpoints are the only places where the original point can move. Then the problem is transformed from point matching to edge matching.
The new graph is called the formal graph, and the original graph is called the coordinate graph.
For the edge matching of formal graph, if the two edges want to match, there must be a common point for connection. This common definition starts from reality and does not mean that the two edges are next to each other. As shown in the figure
Suppose that countless rays are emitted from the origin, one of which is the last three points collinear. If the two edges in the formal graph are just connected, the connection point is considered to be a common point. Note that such a point is the pivot point in the formal diagram. For this problem, the point with the same tan value is the pivot point. (as shown in the figure above, the other two connection points on the ray except the origin are common points)
The calculation method of tan value of the end point of the edge on the formal graph is shown in the following figure:
③ Matching with dfs is also the difficulty of this problem
Let's review some aspects of dfs
https://oi-wiki.org/graph/scc/
There is a property that the two ends of the atavistic edge (backward edge) have different depths in dfs, and the atavistic edge will go to a point with smaller depth.
Then there is a relatively simple greedy strategy, as shown in the figure
Each of the above nodes is a pivot point on the formal graph. The undirected edge of 8 to 3 is a atavistic edge. The greedy strategy is that for a point, if the number of edges under its jurisdiction (including branch edges and atavistic edges, the same below) is even, the point is taken as a common point to make the edges under its jurisdiction match each other. If it is an odd number, the extra edge in the subordinate edge is matched with the edge from the parent node of the point to the point (hereinafter defined as a direct edge). Then, if the number of edges under a node is even, its directly connected edges are reserved for its parent node.
In the process of dfs downward, it only passes through the Branch edge, so other types of edges should be specially treated, because the atavistic edge also belongs to the subordinate edge.
Then, for two special edge processing methods:
Because the atavistic edge will go to a point with less depth, you can tell whether this edge is a cross edge or a atavistic edge.
Cross edge: no traversal
Atavism edge: it belongs to its ancestor, because it is also an edge under the jurisdiction of the ancestor node, and can also participate in matching
3, Codes and notes
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 #include<vector> 6 #include<map> 7 #define LL long long 8 using namespace std; 9 const int N=500050; 10 const int M=N<<2; 11 struct Fraction{ //The first is the fractional structure 12 LL p,q; 13 bool operator <(const Fraction &t) const{ //Remember to put it in the map to be comparable 14 return (long double)p/q <(long double)t.p/t.q; 15 } 16 }; 17 LL gcd(LL x,LL y){ 18 return y==0 ? x : gcd(y,x%y); 19 } 20 Fraction reduce(Fraction a){ //About points 21 LL temp=gcd(a.p,a.q); 22 Fraction fraction={a.p/temp,a.q/temp}; 23 return fraction; 24 } 25 map<Fraction,int> dict; //Map a dictionary 26 int n; 27 int cnt; 28 int get_id(Fraction x){ 29 if(dict.count(x)) return dict[x]; 30 dict[x]=++cnt; 31 return cnt; 32 } 33 int h[M],e[M],ne[M],idx; //Establish formal diagram 34 int G[M]; 35 void add(int a,int b,int id){ 36 e[idx]=b,ne[idx]=h[a],h[a]=idx; 37 G[idx]=id; 38 idx++; 39 } 40 void read(){ 41 memset(h,-1,sizeof(h)); 42 scanf("%d",&n); 43 for(int i=1;i<=n;i++){ 44 LL p1,q1,p2,q2; 45 scanf("%lld%lld%lld%lld",&p1,&q1,&p2,&q2); 46 Fraction x=reduce((Fraction){p1,q1}); //x and y are the two coordinates of the points on the original drawing 47 Fraction y=reduce((Fraction){p2,q2}); 48 Fraction a,b; //A and b are tan values corresponding to the two endpoints of the edge of the formal graph, a is upward and b is rightward 49 a.p=(y.p+y.q)*x.q; 50 a.q=y.q*x.p; 51 b.p=y.p*x.q; 52 b.q=y.q*(x.p+x.q); 53 int u=get_id(a); 54 int v=get_id(b); 55 //cout<<u<<' '<<v<<endl; 56 //cout<<a.p<<' '<<a.q<<' '<<b.p<<' '<<b.q<<endl; 57 add(u,v,i); 58 add(v,u,i); 59 } 60 } 61 int depth[N]; 62 vector<int> edges[N]; //The point on the original graph corresponding to the edge with point i as the connection point on the stored formal graph. Since each pair of matching edges must have the same connection point, the final answer is edges array size / 2 63 void dfs(int u,int fa){ 64 int pre_node=-1; //The point number of the original graph corresponding to the edge of the formal graph corresponding to the endpoint of the directly connected edge 65 depth[u]=depth[fa]+1; 66 for(int i=h[u];~i;i=ne[i]){ 67 int j=e[i]; 68 if(!depth[j]) dfs(j,u); //If you arrive at a point that has not been passed, then go back to dfs (when you write here, accidentally write u and j in reverse, then tune it for more than an hour, then remember to read bug first. 69 else{ //Otherwise, it is a cross edge or a atavistic edge 70 if(depth[j]>depth[u]) continue; //Transverse fork edge 71 else if(j==fa && pre_node==-1){ //The first straight edge is counted as the straight edge, and the other heavy edges are counted as the atavistic edge 72 pre_node=G[i]; 73 } 74 else{ 75 edges[j].push_back(G[i]); //Atavistic edge 76 } 77 } 78 } 79 if(fa){ //Consider straight edges 80 if(edges[u].size() & 1){ //As in the above algorithm analysis, the branch edges are divided into odd and even numbers 81 edges[u].push_back(pre_node); 82 } 83 else{ 84 edges[fa].push_back(pre_node); 85 } 86 } 87 } 88 void print(){ 89 int res=0; 90 for(int i=1;i<=cnt;i++){ 91 res+=edges[i].size()/2; 92 } 93 cout<<res<<endl; 94 for(int i=1;i<=cnt;i++){ 95 for(int j=0;j+1<edges[i].size();j+=2){ 96 printf("%d %d\n",edges[i][j],edges[i][j+1]); 97 } 98 } 99 } 100 int main(){ 101 102 read(); 103 for(int i=1;i<=cnt;i++) 104 if(!depth[i]) dfs(i,0); //Graphs are not necessarily connected 105 print(); 106 107 108 return 0; 109 110 }
(it was written in the trumpet before, but now the trumpet is back, so I move back)