cf1519E

题意

Link

给你 \(n\) 个点,\(1\leq x,y\leq10^9\)

\((x',y')\) 表示为 \((x+1,y)\) 或者 \((x,y+1)\)

定义一对点合法 \((x_1,y_1),(x_2,y_2)\) 当且仅当存在 \((x_1',y_1'),(x_2',y_2')\) 使得\((x_1',y_1'),(x_2',y_2'),(0,0)\) 共线。

问最多能选出多少对点,注意:一个点最多只能被选择一次。

题解

很显然如果 \((x_1,y_1),(x_2,y_2)\)\((0,0)\) 贡献,当且仅当这两个点斜率相同,即 \(\frac{y_1}{x_1}=\frac{y_2}{x_2}\)

而这个 \(\frac{x_1}{y_1}\) 相当于 这个点的属性值。

那么问题转换为给你 \(n\) 个点,每个点有两种可能的属性值。

让你每次选出两个存在相同属性的点,问最多选多少个。


我最初的想法是想把 这个平面上的 \((x,y)\) 看成点,而选择一对 \((x,y)\) 看成一个类似匹配的东西。

但是 1.5h 我也没什么有价值的idea。(如果能类似这么做还请大佬不吝赐教)


正解是这样的,我们把 斜率看成点,而把这个 \((x,y)\) 看成连接两个可能的斜率的边。

我们发现,两个移动后可能共线的 \((x,y)\) 必定是图中连接3个点的两条边,也就是至少有一个公共点的两条边。

现在做法应该比较明晰了。

注意我们的匹配不是针对点的,而是针对边的。

具体做法是建出 \(dfs\) 树,我们把结点连向儿子的边匹配,如果剩下一条边的话 即 \(sz_x\bmod 2=1\)。我们就将剩下这条与 \((x\rightarrow son_x)\) 这条边匹配,并且在 \(fa_x\) 的匹配时不计算这条边。

容易发现这样匹配一定是最优的。

\(why?\) 考虑如果没有全部匹配,一定是只剩下 \(root\rightarrow son_r\) 这一条边。那么发现总边数也一定是奇数,不存在全部匹配的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<bits/stdc++.h>
using namespace std;
int n;
#define int long long
const int N=2e5+10000;
int a[N],b[N],c[N],d[N];
struct frac{int a,b;};
inline bool operator < (frac a,frac b){
if(a.a==b.a) return a.b<b.b;
return a.a<b.a;
}
inline int gcd(int a,int b){
if(b==0) return a;
return gcd(b,a%b);
}
inline frac operator / (frac a,frac b){
frac c;
c.a=a.a*b.b;
c.b=a.b*b.a;
int g=gcd(c.a,c.b);
c.a/=g,c.b/=g;
return c;
}

map<frac,int> mp;

frac x,y,f[N][5];
struct node{int x,v;};
inline bool operator <(node a,node b){
if(a.v==b.v) return a.x<b.x;
return a.v<b.v;
}
vector<pair<int,int> > ans,v[N<<2];
int tot;bool vis[N<<2];
int dep[N<<2];
inline void dfs(int x,int fa,int faid)
{
dep[x]=dep[fa]+1;
for(int i=0;i<v[x].size();i++){
int y=v[x][i].first,w=v[x][i].second;
if(dep[y]==0) dfs(y,x,w);
}
int pre=0;
for(int i=0;i<v[x].size();i++){
int y=v[x][i].first,w=v[x][i].second;
if(dep[y]>dep[x]){
if(vis[w]) continue;
if(pre==0) pre=w;
else{
vis[pre]=vis[w]=1;
ans.push_back(make_pair(pre,w));
pre=0;
}
}
}
if(pre&&faid){
vis[pre]=vis[faid]=1;
ans.push_back(make_pair(pre,faid));
}
}
signed main(){
ios::sync_with_stdio(false);
cin>>n;
int id=0;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>c[i]>>d[i];
x=frac{a[i]+b[i],b[i]};
y=frac{c[i],d[i]};
f[i][1]=x/y;
x=frac{a[i],b[i]};
y=frac{c[i]+d[i],d[i]};
f[i][2]=x/y;
for(int k=1;k<=2;++k){
if(mp[f[i][k]]==0) {tot++,mp[f[i][k]]=tot;continue;}
}
v[mp[f[i][1]]].push_back(make_pair(mp[f[i][2]],i));
v[mp[f[i][2]]].push_back(make_pair(mp[f[i][1]],i));
}
for(int i=1;i<=tot;i++) if(dep[i]==0) dfs(i,0,0);
cout<<ans.size()<<endl;
for(auto x:ans){
cout<<x.first<<" "<<x.second<<endl;
}
}

cf1519E
https://proton-z.github.io/2021/04/15/cf1519E/
作者
Imitators
发布于
2021年4月15日
许可协议