A. Orac and Factors
题目描述:For n≥2, we will denote as f(n) the smallest positive divisor of n, except 1.定义f(n) 为n的最小因子,除1以外。
Now, for two positive integers n and k, Orac asked you to add f(n) to n exactly k times (note that n will change after each operation, so f(n) may change too) and tell him the final value of n.
2.给定n和k,求n=n+f(n),一共k次。
分析:
随便分析几个数可得,n+f(n)会变成偶数。因此f(n)会变成2;
当n不为偶数时,需要运用唯一分解定理,即确定其最小质因子,f(n)+n就会变成偶数。后面每一次相当于加上2;
//AC代码;
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e6+55;
int prime[N];
bool p[N];
int cnt=0;
void deal()
{
for(int i=2;i<N;++i){
if(!p[i]){
prime[cnt++]=i;
}
for(int j=0;j<cnt&&i*prime[j]<N;++j){
p[i*prime[j]]=true;
if(i%prime[j]==0) break;
}
}
}
int main()
{
int t,n,k;
ll ans;
deal();
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
if(n%2==0){
ans=n+2*k;
}else{
int x;
for(int i=0;i<cnt;++i){
if(n%prime[i]==0){
x=prime[i];
break;
}
}
ans=n+x+2*k-2;
}
printf("%lld\n",ans);
}
return 0;
}
B. Orac and Models
题目描述:
There are n models in the shop numbered from 1 to n, with sizes s1,s2,…,sns1,s2,…,sn.
Orac thinks that the obtained arrangement is beatiful, if for any two adjacent models with indices ij and ij+1(note that ij<ij+1, because Orac arranged them properly), ij+1 is divisible by ij and sij<sij+1.
给定一个数组,求数组下标成倍数的最长上升序列。
分析:
对于任意一个数,他有几个因子,就可能有几个上升序列。例如:20:1,2,4,5,10,都是他的因子。 (1≤n≤100000),根据数据范围可以将每一个点记录其最长上升序列,即dp[i]表示i之前的是i的因子的最长上升序列。
现在我们就需要解决如何将一个数的因子全部分解出来。我用了枚举的方法
即1、2、3、、、sqrt(n)。
//状态方程
if(num[i]>num[k]) dp[i]=max(dp[i],dp[k]+1);
//AC代码;
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e5+55;
int num[N],dp[N];
int main()
{
int t,n,k;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&num[i]);
dp[i]=1; //初始化dp[i]为1,每一个数本身都算一个;
}
for(int i=2;i<=n;++i){
int up=(int) sqrt(i); //寻找所有因子;
for(int j=1;j<=up;++j){
if(i%j==0){
k=i/j; //因子成对,即a*b=i,减少了时间复杂度,只需要找到sqrt(i);
if(num[i]>num[k]) dp[i]=max(dp[i],dp[k]+1);
if(num[i]>num[j]) dp[i]=max(dp[i],dp[j]+1);
}
}
}
int maxn=0;
for(int i=1;i<=n;++i)
maxn=max(maxn,dp[i]);
printf("%d\n",maxn);
}
return 0;
}
C. Orac and LCM
题目描述:
一串数{s1,s2,s3…},求这串数字中每对数的最小公倍数的最小公因子。For example, gcd({8,12})=4, gcd({12,18,6})=6, gcd({8,12})=4, gcd({12,18,6})=6 and lcm({4,6})=12, lcm({4,6})=12.
分析:
对于每对数(a,b)最小公倍数来说,包含这两个数共有部分和每一个数的特有因子即: gcd(a,b)和a/gcd(a,b)、b/gcd(a,b);当n个数两两组合之后得到一组最小公倍数数集,再求这些数的最大公因子。相当于就是求这串数的共有部分相乘。由于是两两组合,所以只需要共有部分有n-1就行了。
例如:10 24 40 80 这4个数,分解开来就是 25、3222、5222、5222*2、5出现了3次,2出现了4次,4出现了3次,8出现了3次,但由于2、4可以属于8,因此因子就可以有5、8两个,也就是40;
//AC代码;
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=2e5+55;
int p[N],prime[N];
bool pp[N];
void deal() //这儿用素数去分解它的因子,反正就是要找到所有公共因子
{
int cnt=0;
for(int i=2;i<N;++i){
if(!pp[i]){
prime[cnt++]=i;
}
for(int j=0;j<cnt&&prime[j]*i<N;++j){
pp[prime[j]*i]=true;
if(i%prime[j]==0) break;
}
}
}
int main()
{
int n,x,y;
deal();
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%d",&x);
int j=0,k=0;
while(x>1){
while(x%prime[j]!=0){
++j;
if(prime[j]*prime[j]>x) {
k=1;
break;
}
}
if(k){
p[x]++;
break;
}
int cnt=1;
while(x%prime[j]==0){
cnt*=prime[j];
p[cnt]++; //每一个因子都要算进去,但后面以满足条件的最大因子为准。
x/=prime[j];
}
}
}
n--;
ll ans=1;
for(int i=N-22;i>1;--i){ //从最后面开始遍历,大的将小的包含了。
if(p[i]>=n&&ans%i!=0) ans*=i;
}
printf("%lld\n",ans);
return 0;
}
剩余的题也尽量补