梦熊2024--七月份--S组
A
考虑什么情况可以走到所有的点。
如果用能走\(a\)和\(b\)的点,那么一定可以走\(gcd(a,b)\),所以\(gcd=1\)的数们可以走到所有的点
那么为什么能走\(a\)和\(b\)的点,那么一定可以走\(gcd(a,b)\)呢?
先假设就是刚刚的\(a\)和\(b\),\(g=gcd(a,b)\),那么往这边走\(x\)个\(a\),再往那边走\(y\)个\(b\),就是要证明\(xa-yb=g\)。
\(a\)可以写成\(kg\),\(b\)可以写成\(lg\),那么就是要证明\(xkg-ylg=g\),\((xk-yl)g=g\),\(xk=yl\)即可。
所以我们只需要求几个\(x_i\)的最大公因数为\(1\)的\(c_i\)的和最小即可。
那么我们可以开一个数组\(dp_k\)表示以\(k\)为最大公因数的数们的\(c_i\)最小和,最终答案就是\(dp_1\)。
那么我们对于每一个\(x_i\)每次取出之前做过\(dp\)的数(用一个\(vector\)存下来),与他取最大公因数,得到数的价值就是那个之前做过\(dp\)的数的价值加\(c_i\)。
注意这个是\(map\),无法赋最大值后去\(min\),就要判断一下这个点是否存过数,如果存过再取\(min\),否则直接赋值。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
int x[1005],c[1005];
int gcd(int x,int y){
return y == 0?x:gcd(y,x%y);
}
map<int,int> dp,mp;
signed main(){
cin >> n;
int tmp = 0;
for(int i = 1;i <= n;++ i){
cin >> x[i];
tmp = gcd(x[i],tmp);
}
for(int i = 1;i <= n;++ i){
cin >> c[i];
}
if(tmp != 1){
cout << "imposib1e";
return 0;
}
vector<int> s;
s.push_back(0);
mp[0] = 1;
for(int i = 1;i <= n;++ i){
for(int j = 0;j < s.size();++ j){
int w = s[j];
int t = gcd(w,x[i]);
dp[t] = dp[t] == 0?dp[w]+c[i]:min(dp[t],dp[w]+c[i]);
if(mp[t] != 1){
mp[t] = 1;
s.push_back(t);
}
}
}
cout << dp[1];
return 0;
}
B
先明确一个条件:虽然他说只能向后走,但其实我们可以向前走并不花时间,因为如果我们想向前走,只要在那个点的时候多停一会即可,就相当于向前走了。
那么考虑一下前两个特殊条件。
\(1.\)\(T_i=0\)。
\(T_i=0\)也就是说走不花时间,所以我们可以将\(m\)的时间全部花在取快乐值上。那么我们可以用一个大根堆,一开始把所有的点的初始快乐值加到堆里,每次取出最大的加入答案中,并把减去\(D_i\)的数再加进去。为什么呢?因为我们刚刚说了,向前走不花时间,这个向后走也不花时间,那么就可以随意窜来窜去。
\(2.\)\(D_i=0\)
这样,我们就可以在同一个点上待上很久,并一直取他,因为他的快乐值不会变小。那么我们枚举待在哪个点,看看走到这个点要多久,剩下的时间就可以一直取他喽。这里延伸一下,我们从这里可以看出取快乐值要有一个范围,如\(1~w\),因为加到\(t_{w}\)(走到\(w+1\))后就\(>m\)了走都走不到,只能走到\(w\),后面的值是取不到的。
那么特殊条件考虑完了,在考虑一下正解。
我们以第一个特殊条件为主出发去考虑