A. Orac and Factors:
题目大意:
k k k次操作,给 n n n每次加上其的最小因数( n n n会改变), k k k次操作之后,输出 n n n
解题思路:
如果
n
n
n为奇数,那么其最小因数肯定为奇数,相加之后的
n
n
n即为偶数,而偶数的最小因子为2,之后
n
n
n一直为偶数
因此只要在一开是
f
o
r
for
for一遍找出最小因子,加上后,加上
(
k
−
1
)
∗
2
(k-1)*2
(k−1)∗2即可
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,n,k;
int main() {
scanf("%lld",&t);
while(t--) {
scanf("%lld%lld",&n,&k);
for(int i=2; i<=n; i++)
if(n%i==0) {
n+=i;
break;
}
n+=(k-1)*2;
printf("%lld\n",n);
}
}
B. Orac and Models
题目大意:
长度为 n n n的序列,问取出最长的子序列满足以下条件,相邻标号,右边的序号是左边的倍数(在原序列中),且值为递增。
解题思路:
- 记忆话搜索
- dp+预处理所有数的因子(
嫌麻烦,于是比赛时没写)
AC代码:
#include<bits/stdc++.h>
using namespace std;
int t,n;
int a[100100],f[100100];//f数组表示以当前位置的数为开头的最长子序列
int dfs(int pos) {
//system("pause");
if(f[pos]) return f[pos];//搜过就不搜了
f[pos]=1;
for(int i=2; i<=n; i++) {
if(pos*i>n) break;
if(a[pos*i]>a[pos]) {//吐槽:一开始在这里,一直写i一直错....
f[pos]=max(f[pos],dfs(pos*i)+1);
}
}
return f[pos];
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=1; i<=n; i++)
if(!f[i]) f[i]=dfs(i);
int ans=0;
for(int i=1; i<=n; i++) {
ans=max(ans,f[i]);
f[i]=0;
}
printf("%d\n",ans);
}
}
C. Orac and LCM
题目大意:
n n n个数,分别为a1,a2…an,构造一个 b b b序列,其中元素为:lcm(a1,a2),lcm(a1,a3)…lcm(a1,an),lcm(a2,an)…,lcm(an-1,an),问这些数的最大公约数是什么?
解题思路:
最后一秒钟没交上去…第二天就A了…
第一种方法:
g
c
d
(
l
c
m
(
a
,
b
)
,
l
c
m
(
a
,
c
)
)
=
a
∗
g
c
d
(
b
,
c
)
/
g
c
d
(
a
,
b
,
c
)
gcd(lcm(a,b),lcm(a,c))=a*gcd(b,c)/gcd(a,b,c)
gcd(lcm(a,b),lcm(a,c))=a∗gcd(b,c)/gcd(a,b,c)
什么要证明?对不起,不会ing…留坑
填坑:
g
c
d
(
l
c
m
(
a
,
b
)
,
l
c
m
(
a
,
c
)
)
=
a
∗
g
c
d
(
b
,
c
)
/
g
c
d
(
a
,
b
,
c
)
gcd(lcm(a,b),lcm(a,c))=a*gcd(b,c)/gcd(a,b,c)
gcd(lcm(a,b),lcm(a,c))=a∗gcd(b,c)/gcd(a,b,c)与
g
c
d
(
l
c
m
(
a
,
b
)
,
l
c
m
(
a
,
c
)
)
=
l
c
m
(
a
,
g
c
d
(
b
,
c
)
)
gcd(lcm(a,b),lcm(a,c))=lcm(a,gcd(b,c))
gcd(lcm(a,b),lcm(a,c))=lcm(a,gcd(b,c))等价
1.将
l
c
m
(
x
,
y
)
lcm(x,y)
lcm(x,y)看作x和y质因子的并集,
g
c
d
(
x
,
y
)
gcd(x,y)
gcd(x,y)看作x和y质因子的交集
2.
左
式
=
(
A
⋃
B
)
⋂
(
A
⋃
C
)
左式=(A \bigcup B) \bigcap (A \bigcup C)
左式=(A⋃B)⋂(A⋃C)
3.
右
式
=
A
⋃
(
B
⋂
C
)
右式=A \bigcup (B \bigcap C)
右式=A⋃(B⋂C)
4.用点集合知识就能证明
左
式
=
右
式
左式=右式
左式=右式
其余做法:
- p k ∣ a n s p^k|ans pk∣ans当且仅当至少有 n − 1 n-1 n−1个整数在 a a a数组中满足 p k − 1 ∣ a p^{k-1}|a pk−1∣ai
- s o l u t i o n 1 solution ~1 solution 1:找到这样一个 v v v,列举每一个小于等于 p p p的质数,计算最大的 k k ki使pki<=ai,其中排名第二小的ki即满足 p k i ∣ a n s p^{ki}|ans pki∣ans(根据1),算出所有的 a n s ans ans的质因数相乘即可
- s o l u t i o n 2 solution ~2 solution 2:大致思路:设di为除ai之外的元素,gcd(di)又满足条件1,因此ans=lcm(gcd(d1,d2,…,dn))
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
ll a[100100], d[100100], b[100100];
ll Gcd(ll a, ll b) {
if(b == 0) return a;
else return Gcd(b, a%b);
}
int main() {
scanf("%lld", & n);
for(int i = 1; i <= n; i++) scanf("%d", & a[i]);
d[n] = a[n];//d[i]数组计算a[n]~a[i]的最大公约数
for(int i = n-1; i >= 1; i--) d[i] = Gcd(d[i+1], a[i]);
for(int i = 1; i <= n; i++) b[i] = a[i] * d[i+1] / d[i];
ll ans = b[n];
for(int i = 1; i <= n-1; i++) ans = Gcd(ans, b[i]);
cout << ans <<endl;
}
D. Orac and Medians
题目大意:
一个数组里一段数可以都变成他的中位数,问整个序列中的数能不能都变成 k k k。可以变化很多次。(偷懒ing)
解题思路:
- 首先这个序列中要有 k k k
- 其次是存在相邻的两个元素大于等于 k k k或者存在间隔一个元素的两个元素大于等于 k k k
- ps:这个很好手模,请各位读者手模证明一下,加深印象
AC代码:
#include<bits/stdc++.h>
using namespace std;
int t, n, k, tag1, tag2;
int a[100100];
int main() {
scanf("%d", & t);
while(t--) {
scanf("%d%d", & n, & k);
for(int i = 1; i <= n; i++) scanf("%d", & a[i]);
tag1 = tag2 = 0;//tag1代表是否满足条件1,tag2代表是否满足条件2
for(int i = 1; i <= n; i++) {
if(a[i] == k) tag1 = 1;
if(a[i] >= k && a[i + 1] >= k || a[i - 1] >= k && a[i + 1] >= k) tag2 = 1;
}
if(tag1 && tag2 || n == 1 && a[1] == k) printf("YES\n");
//注意特判n = 1的情况
else printf("NO\n");
for(int i = 1; i <= n; i++) a[i] = 0; //初始化
}
return 0;
}