题目传送门
这道题很巧妙
给你n组数据,
c
[
i
]
c[i]
c[i]代表的是可以向前或者向后走
c
[
i
]
c[i]
c[i]步,而
l
[
i
]
l[i]
l[i]就是获得
c
[
i
]
c[i]
c[i]这个卡片所需要的花费,现在你需要用这些卡片中的若干个可以走到实数集上的每一个位置,问你的最小花费是多少?
这道题有一个很明显的思路就是裴蜀定理:
那么也就是说只要
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1那么我就可以实现题目的要求。因为:
k
∗
(
a
x
+
b
y
)
=
k
∗
g
c
d
(
a
,
b
)
=
k
,
k*(ax+by)=k*gcd(a,b)=k,
k∗(ax+by)=k∗gcd(a,b)=k,
k
k
k是任意实数。那么本问题就转化成为了:找到若干个互质的数需要的最小花费。
那么接下来就可以把本问题抽象成为一张图:
两两
l
[
i
]
l[i]
l[i]的
g
c
d
gcd
gcd可以知道,
g
c
d
gcd
gcd和原点的距离是他们的花费数,最后求
g
c
d
=
1
gcd=1
gcd=1的最小距离,因为这些
g
c
d
(
0
,
x
)
=
x
,
gcd(0,x)=x,
gcd(0,x)=x,而且
0
0
0点无花费,因此可以看作是求最后1到0的最短距离。
#include<iostream>
#include<queue>
#include<map>
#include<vector>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef pair<ll,ll>pii;
unordered_map<ll,ll>vis;
unordered_map<ll,ll>dis;
ll c[2102120],l[2102010];
inline ll getint()//这个是读入优化,实测速度比scanf快
{
char ch;
int res=0;
while(ch=getchar(),ch<'0'||ch>'9');
res=ch-48;
while(ch=getchar(),ch>='0'&&ch<='9')
res=(res<<3)+(res<<1)+ch-48;
return res;
}
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
int main(){
ll n;
n=getint();
for(ll i=1;i<=n;i++) l[i]=getint();
for(ll i=1;i<=n;i++) c[i]=getint();
dis[0]=0;
priority_queue<pii, vector<pii>, greater<pii>> heap;
heap.push({0,0});
while(heap.size()){
auto t=heap.top();
heap.pop();
ll dist=t.first;
ll num=t.second;
if(vis.find(num)!=vis.end())continue;
else vis[num]=1;
for(ll i=1;i<=n;i++){
ll u=gcd(num,l[i]);
if(dis.find(u)==dis.end())dis[u]=0x3f3f3f3f;
if(dis[u]>dist+c[i]){
dis[u]=dist+c[i];
heap.push({dis[u],u});
}
}
}
if(dis[1]==0x3f3f3f3f||dis[1]==0)cout<<"-1"<<endl;
else cout<<dis[1]<<endl;
return 0;
}