21牛客多校3B - Black and white
p
r
o
b
l
e
m
:
problem:
problem:
现在要涂黑一个
n
∗
m
(
n
,
m
≤
5000
)
n*m(n,m≤5000)
n∗m(n,m≤5000) 的棋盘上,涂黑第
(
i
,
j
)
(i,j)
(i,j) 个格子需要
c
(
i
,
j
)
c(i,j)
c(i,j) 的花费,并且对于任意两行两列交错形成的四个格子,如果其中的三个已经被涂黑,那么第四个可以免费涂黑,求最小代价
s
o
l
u
t
i
o
n
:
solution:
solution:
首先从贪心的角度出发,我们按权值从小到大取格子,当这个格子满足条件时免费。但是如何去判断一个格子是否免费呢?暴力判断?看这个格子所在行列上是否已经有格子被涂黑?这些显然都不行,我们根据题意转换:设当前格子在
(
x
0
,
y
0
)
(x_0,y_0)
(x0,y0),同行上有
(
x
0
,
y
)
(x_0,y)
(x0,y),同列上有
(
x
,
y
0
)
(x,y_0)
(x,y0),那么必须
(
x
,
y
)
(x,y)
(x,y) 已经被涂黑,这个格子才能免费。我们可以想到用并查集去维护这个联通关系,也就是行与列的联通关系,
(
x
,
y
)
(x,y)
(x,y) 这个点涂黑代表着
x
x
x 行与
y
y
y 列的联通,且如果当前点的行列已经联通,我们显然就不需要花费了。
那么这个问题显然变成了 n + m n+m n+m 个点( n n n 行 m m m 列)联通的最小花费,且点 ( x , y ) (x,y) (x,y) 相当于一条连接 x x x 与 y + n y+n y+n 权值为 c ( x , y ) c(x,y) c(x,y) 的边,那么我们可以用最小生成树来解决这个问题,并且由于是稠密图,我们用 p r i m prim prim 效果会更好,但是由于权值范围 [ 0 , 1 e 5 ) [0,1e5) [0,1e5) 我们可以直接以权值为动态数组下标存边,然后进行 k r u s k a l kruskal kruskal 算法判断连通性。
i
d
e
a
:
idea:
idea:
这题的要点在于将问题转化到 ”连通块“ ”并查集“ 上,并且这类问题通常要有将点转化为边的思想,以这题为例,二维坐标上的点联通着
x
,
y
x,y
x,y 坐标,那么这就是一条连接两点的边
CF1519E - Off by One
p
r
o
b
l
e
m
:
problem:
problem:
在一个二维平面上,第
i
i
i 个点在坐标
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi) 位置,现在可以进行如下操作:
- 选择两个点 a , b ( a ≠ b ) a,b (a≠b) a,b(a=b)
- 将点 a a a 从 ( x a , y a ) (x_a,y_a) (xa,ya) 移动至 ( x a + 1 , y a ) (x_a+1,y_a) (xa+1,ya) 或 ( x a , y a + 1 ) (x_a,y_a+1) (xa,ya+1)
- 将点 b b b 从 ( x b , y b ) (x_b,y_b) (xb,yb) 移动至 ( x b + 1 , y b ) (x_b+1,y_b) (xb+1,yb) 或 ( x b , y b + 1 ) (x_b,y_b+1) (xb,yb+1)
- 删除点 a , b a,b a,b
但是只有满足移动后的两点 a ′ , b ′ a',b' a′,b′ 与 ( 0 , 0 ) (0,0) (0,0) 三点都在一条直线上,才能进行此操作,并且删除后点将不能再进行任何操作,求可以进行的最多操作数,以及每次操作的对象
s
o
l
u
t
i
o
n
:
solution:
solution:
三个点在一条直线上即
a
,
b
a,b
a,b 两点与
(
0
,
0
)
(0,0)
(0,0) 构成的直线的斜率相同。我们将每个点看作一条边,即连接斜率
y
+
1
x
y
x
+
1
\frac {y+1}x \frac y{x+1}
xy+1x+1y 这两个点的一条边,那么最后构成了一张图,且我们要在这张图上找最多的对数,满足:两条边共用一个点,且每条边至多使用一次。
这个问题的答案很显然,设一个连通块的边数为 m i m_i mi,那么答案就是 ∑ ⌊ m i 2 ⌋ \sum \lfloor \frac {m_i}2 \rfloor ∑⌊2mi⌋。但是去求方案,这是一个非常困难的问题,除非我们借助 d f s dfs dfs树。我们对于每个连通块构造 d f s dfs dfs树,接着我们发现这个问题在树上很好解决,对于结点 u u u,接着我们递归到子树 v v v中进行匹配,可以发现这个匹配的过程要么多出一条边,要么不多,使可能存在的多出来的那条边与 v v v 相连,那么一旦它确实剩余,我们就用函数返回并且将它与 e ( u , v ) e(u,v) e(u,v) 匹配,否则 e ( u , v ) e(u,v) e(u,v) 则剩余出来。可以发现这个过程至多会产生一条多余的边,我们只需要用一个变量保存这条边的编号,接着十分方便 e ( u , v i ) e(u,v_i) e(u,vi) 之间的匹配,如果最后产生一条我们就从函数返回出去。我们处理完了树边,那么对于其他非树边,我们可以将它定向为走向叶节点的边,那么可以发现这些边走向的子树已经在刚刚被我们匹配完成了,这条边必然剩余,也可以看作上述树边匹配过程时子树全匹配的边 e ( u , v ) e(u,v) e(u,v),也就是说完全可以当作树边处理,只是不用再去递归这条边的子树,至此我们就 O ( n ) O(n) O(n) 完成了整个操作。
c o d e : code: code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int maxn=2e5+10;
ll a[maxn],b[maxn],c[maxn],d[maxn];
int n,cnt,vis[maxn<<1];
int head[maxn],to[maxn<<1],nex[maxn<<1],idx[maxn<<1],tot=0;
inline void add(int u,int v,int id){
to[++tot]=v;
nex[tot]=head[u];
idx[tot]=id;
head[u]=tot;
}
pll f(ll x,ll y){
ll g=__gcd(x,y);
return pll(x/g,y/g);
}
map<pll,int> mp;
vector<int> ans;
int dfs(int u,int fa){
int left=0;
vis[u]=1;
for(int i=head[u];i;i=nex[i]){
int v=to[i];
if(v==fa) continue;
if(vis[v]==1) continue;
if(!vis[v]){
int temp=dfs(v,u);
if(temp){
ans.push_back(temp);
ans.push_back(idx[i]);
}
else{
if(left) ans.push_back(left),ans.push_back(idx[i]),left=0;
else left=idx[i];
}
}
else{
if(left) ans.push_back(left),ans.push_back(idx[i]),left=0;
else left=idx[i];
}
}
vis[u]=2;
return left;
}
int main(){
cin>>n;
int u,v;
for(int i=1;i<=n;i++){
scanf("%lld %lld %lld %lld",&a[i],&b[i],&c[i],&d[i]);
pll x=f((a[i]+b[i])*d[i],b[i]*c[i]);
pll y=f(a[i]*d[i],(c[i]+d[i])*b[i]);
if(!mp[x]) mp[x]=++cnt;
if(!mp[y]) mp[y]=++cnt;
u=mp[x],v=mp[y];
add(u,v,i);
add(v,u,i);
}
for(int i=1;i<=cnt;i++){
if(vis[i]) continue;
dfs(i,0);
}
cout<<ans.size()/2<<endl;
for(int i=0;i<ans.size();i+=2) printf("%d %d\n",ans[i],ans[i+1]);
return 0;
}
i
d
e
a
:
idea:
idea:
一开始看到题,产生了从某个点开始然后尽量去匹配另外点的想法,但是显然这种贪心的匹配方法存在纰漏,即如果不知道往哪个方向移动且不知道用哪个点来和它匹配。这题和上一题一样,都需要把一些不显而易见的问题转化为图论上的问题,并且都是在二维平面上以点作边来建图,形成某个模型,再用我们熟悉的算法来解决。同时这道题的
d
f
s
dfs
dfs树 作为图论的一把利刃在很多方面如二分图,连通图问题上也还有很多用武之地,有待继续深究。
21牛客多校3E - Math
p
r
o
b
l
e
m
:
problem:
problem:
求满足
x
y
+
1
∣
x
2
+
y
2
,
1
≤
x
≤
y
≤
n
xy+1 | x^2+y^2,1≤x≤y≤n
xy+1∣x2+y2,1≤x≤y≤n 的
(
x
,
y
)
(x,y)
(x,y) 的对数
(
n
<
=
1
e
18
)
(n<=1e18)
(n<=1e18)
s
o
l
u
t
i
o
n
:
solution:
solution:
设
x
2
+
y
2
=
k
(
x
y
+
1
)
x^2+y^2=k(xy+1)
x2+y2=k(xy+1),我们如果枚举
x
x
x,即固定一个
x
x
x,那么得到
y
y
y 的方程
y
2
−
k
x
y
+
x
2
−
1
y^2-kxy+x^2-1
y2−kxy+x2−1,根据韦达定理:
y
1
+
y
2
=
b
−
a
=
k
x
y_1+y_2=\frac b{-a}=kx
y1+y2=−ab=kx,那么
(
x
,
k
x
−
y
)
(x,kx-y)
(x,kx−y) 也是一组解。
接着我们通过打表 得到通解都形如
(
x
,
x
3
)
(x,x^3)
(x,x3),得到了此时
k
=
x
2
k=x^2
k=x2 。如果
(
x
,
y
)
(x,y)
(x,y) 是满足题意的一组解显然
x
<
=
y
x<=y
x<=y,那么
x
>
k
x
−
y
x>kx-y
x>kx−y,即第二组解
(
x
,
k
x
−
y
)
(x,kx-y)
(x,kx−y) 不满足条件。可以按照这个特点反向构造出另一组解,通过
(
y
,
x
)
−
>
(
y
,
k
y
−
x
)
(y,x)->(y,ky-x)
(y,x)−>(y,ky−x),显然
(
y
,
k
y
−
x
)
(y,ky-x)
(y,ky−x) 满足条件
y
<
k
y
−
x
y<ky-x
y<ky−x,那么我们可以按照上述过程构造:
(
x
,
x
3
)
−
>
(
x
3
,
x
5
−
x
)
.
.
.
.
(x,x^3)->(x^3,x^5-x)....
(x,x3)−>(x3,x5−x)....,然后把一组解的第二关系放入数组排序后二分即可
- 2020-07-26
- 2020-07-28
- 2020-08-01
- 2020-08-25