G. Short Task
题意:给出一个数字c,让你求最小的N,使得 ∑ d ∣ N = c \sum_{d|N}=c ∑d∣N=c.
思路:首先,我们需要知道一个算术基本定理。
N
=
p
1
c
1
p
2
c
2
p
3
c
3
.
.
p
k
c
k
N={p_1}^{c_1}{p_2}^{c_2}{p_3}^{c_3}..{p_k}^{c_k}
N=p1c1p2c2p3c3..pkck
∑
d
∣
N
=
(
1
+
p
1
+
p
1
2
+
.
.
.
+
p
1
c
1
)
(
1
+
p
2
+
p
2
2
+
.
.
.
+
p
2
c
2
)
.
.
.
(
1
+
p
k
+
p
k
2
+
.
.
.
+
p
k
c
k
)
\sum_{d|N}=(1+p_1+p_1^2+...+p_1^{c_1})(1+p_2+p_2^2+...+p_2^{c_2})...(1+p_k+p_k^2+...+p_k^{c_k})
d∣N∑=(1+p1+p12+...+p1c1)(1+p2+p22+...+p2c2)...(1+pk+pk2+...+pkck)
那么,我们用一个数组sum表示每个数的因数的和。对于N<1e7,我们可以先晒出1- 1 e 7 \sqrt{1e7} 1e7内的所有的素数,对于 1 e 7 \sqrt{1e7} 1e7到1e7之间的所有素数,我们直接sum[i]=i+1即可。我们首先找到每个数对应的最小质数。然后就可以利用上述公式进行计算。最后,我们倒序枚举出现的这个数的最小的位置即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=1e7+10;
ll qk(ll a,ll b,ll p){
if(b==0) return 1;
if(b&1) return a*qk(a,b-1,p)%p;
else{
ll mul=qk(a,b/2,p);
return mul*mul%p;
}
}
int d[N];
int ans[N];
int pnum=0;
ll sum[N];
int main(){
freopen("test.in","r",stdin);
int tmp=sqrt(N);
for(int i=2;i<tmp;i++){ //找出1-N的每个数的最小质因数
if(d[i]==0){
d[i]=i;
for(int j=i*i;j<N;j+=i){
if(d[j]==0){
d[j]=i;
}
}
}
}
sum[1]=1;
for(int i=2;i<N;i++){
if(d[i]==0){ //sqrt(N)之后的素数
d[i]=i;
sum[i]=i+1;
}else{
int j=i;
sum[i]=1;
//算术基本定理求因数之后
while(j%d[i]==0){
j/=d[i];
sum[i]=sum[i]*d[i]+1;
}
sum[i]=sum[i]*sum[j];
}
}
//倒序枚举即可
for(int i=N-1;i;i--){
if(sum[i]<N){
ans[sum[i]]=i;
}
}
int t;
cin>>t;
while(t--){
int c;
cin>>c;
if(ans[c]) cout<<ans[c]<<endl;
else puts("-1");
}
fclose(stdout);
return 0;
}
F. Education
题意:有n个位置,在每个位置每天有一个收益a[i],位置越靠近后面收益越高。同时每个位置有一个到达该位置所需要的花费,从第2个位置开始,到达该位置需要花费一定的费用b[i]。某人最开始在位置1,拥有的金钱为0,它可以待在当前位置,同时也可以通过一定的花费到达下一个位置,但只能一级一级到达。他想获得收益c的所需要的最少的天数。
思路:贪心,很显然。题目规定,越往后面收益值越高,那么我们就尽量到后面收益高的位置。所以,对于每一个位置,有两条路,一条路是我们可以计算假如待在这个位置直到获得收益c的天数。另一条路是我们也要继续往后面走,方便我们计算下一个位置的情况。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=120;
ll qk(ll a,ll b,ll p){
if(b==0) return 1;
if(b&1) return a*qk(a,b-1,p)%p;
else{
ll mul=qk(a,b/2,p);
return mul*mul%p;
}
}
ll a[200010],b[200010],sum[100010];
void solve(){
ll n,c;
cin>>n>>c;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<n;i++){
cin>>b[i];
}
b[n]=0;
ll ans=1e18;
ll sum=0; //目前拥有的钱
ll res=0; //当前花费了多少天
for(int i=1;i<=n;i++){
ans=min(ans,res+max(0ll,c-sum+a[i]-1)/a[i]);
ll need=max(0ll,b[i]-sum+a[i]-1)/a[i];
res+=need+1;
sum+=a[i]*need-b[i];
}
cout<<ans<<endl;
}
int main(){
//freopen("test.in","r",stdin);
int t;
cin>>t;
while(t--){
solve();
}
fclose(stdout);
return 0;
}
这里,我介绍一个比较巧妙的一个地方,我们发现代码中有max(0ll,c-sum+a[i]-1)/a[i]
这种的,其实这是为了我们计算所需要的天数。我们这个题目要求的是向上取整,而我们的C++默认的是向下取整(其实是有向上取整的函数)。然后题目是先减去(a[i]-1),然后后面直接加上1就行了,就不用担心除不尽的情况了。这只是一个小技巧,稍微提醒一下。