A. Orac and Factors
(Description:)
设 (f(n)) 是 (n) 的最小约数 ((f(n) > 1))。那么对于一次操作而言 (n = n + f(n)),求第 (k) 次操作后,(n) 为多少?
(Solution:)
对于 (n) 是偶数的情况下,(f(n) = 2),那么加上 (f(n)) 后 (n) 仍然是偶数,所以每次都 (+ 2)。
对于 (n) 是奇数的情况下,(f(n)) 也一定是奇数,那么加上 (f(n)) 后,(n) 就变成了偶数。
(Code:)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int t; cin >> t;
while(t --){
ll n, k;
cin >> n >> k;
if(n & 1){
int mark = 0;
for(int i = 2; i * i <= n; i ++){
if(n % i == 0){
n += i;
mark = 1;
break;
}
}
if(!mark) n += n;
cout << n + (k - 1) * 2 << endl;
}else{
cout << n + k * 2 << endl;
}
}
return 0;
}
B. Orac and Models
(Description:)
给定一个长为 (n) 的数组 (a),在数组挑选几个数组成一个新序列,要求新序列的任意两个数要满足:(j mod i = 0 and a_i < a_j),(i, j) 是该数在原数组的下标。
(Solution:)
设 (f[i]) 为以 (a_i) 结尾的构成合法新序列的最长长度。那么我们去枚举 (i) 的约数 (j),于是得到:(f[i] = max(f[i], f[j] + 1))。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
int s[N], f[N];
int main(){
int t; cin >> t;
while(t --){
cin >> n;
for(int i = 1; i <= n; i ++)
scanf("%d", &s[i]);
for(int i = 1; i <= n; i ++){
f[i] = 1;
for(int j = 1; j * j <= i; j ++){
if(i % j == 0){
if(s[i] > s[j])
f[i] = max(f[i], f[j] + 1);
if(s[i] > s[i / j])
f[i] = max(f[i], f[i / j] + 1);
}
}
}
cout << *max_element(f + 1, f + n + 1) << endl;
}
return 0;
}
C. Orac and LCM
(Description:)
给定长度为 (n) 的数组 (a),求由 ((lcm(a_i, a_j) | i < j)) 得到一些数的 (gcd)。
(Solution:)
单独看 (a_1),我们可以得到 (lcm(a_1,a_2),lcm(a_1,a_3),...,lcm(a_1,a_n))。则:
我们预处理出一个 (gcd) 的后缀,就可以简单的得出结果。
证明一下上面的公式:
假设:(lcm(a_1, a_2) = a_1 imes a_2 imes x_2),那么 (x_2 = frac{1}{gcd(a_1,a_2)});
同理:(lcm(a_1,a_3) = a_1 imes a_3 imes x_3);那么 (x_3 = frac{1}{gcd(a_1,a_3)});
那么设: (t = gcd(lcm(a_1,a_2),lcm(a_1,a_3)) = gcd(a_1 imes a_2 imes x_2, a_1 imes a_3 imes x_3) = a_1 imes gcd(a_2 imes x_2, a_3 imes x_3))。
因为:(gcd(ab, m) = gcd(a, m) imes gcd(b, m));
所以:(t = a_1 imes gcd(a_2, a_3) imes gcd(x_2, x_3) imes gcd(a_2,x_3) imes gcd(a_3, x_2));
由于:(gcd(frac{1}{a}, frac{1}{b}) = frac{1}{gcd(a,b)})
则:(gcd(a_2, x_3) = gcd(frac{a_2}{1}, frac{1}{gcd(a_1,a_3)}) = frac{gcd(a_2,1)}{gcd(1,gcd(a_1,a_3))} = 1);
同理:(gcd(a_3,x_2) = 1);
那么:
(t = a_1 imes gcd(a_2,a_3) imes gcd(x_2, x_3) = frac{a_1 imes gcd(a_2, a_3)}{gcd(gcd(a_1,a_2), gcd(a_1,a_3))});
由:(gcd) 满足交换律:(gcd(gcd(a,b), gcd(a,c)) = gcd(a, a, b, c) = gcd(a, b, c) = gcd(a, gcd(b, c)));
那么:(t = frac{a_1 imes gcd(a_2,a_3)}{gcd(a_1,gcd(a_2,a_3))} = lcm(a_1,gcd(a_2,a_3))).
证毕。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
int n;
ll a[N], g[N];
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { return a * b / gcd(a, b); }
int main(){
cin >> n;
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
g[n + 1] = 0;
for(int i = n; i >= 1; i --)
g[i] = gcd(g[i + 1], a[i]);
ll ans = 0;
for(int i = 1; i < n; i ++){
ll tmp = lcm(a[i], g[i + 1]);
ans = gcd(ans, tmp);
}
cout << ans << endl;
return 0;
}
D. Orac and Medians
(Description:)
给定长为 (n) 的数组 (a) 和一个 (k),,问是否可以通过任意次操作将数组每个元素变为 (k)?
操作是:将数组区间 ([l, r]) 的数变为该区间的中位数。
(Solution:)
很显然的一个判断是确认数组中有没有 (k) 这个元素。
对于中位数的确认,显然涉及到了排序,但是时间不允许。但是如果我们只考虑长度 (2) 的区间,显然小的那个数就是中位数。那么对于固定的 (k) 来说,我们只需要他的左边或右边存在一个 (geq k) 的数,那么就可以变为 (k),那么此时我们就得到了两个连续的 (k),那么我们在这个区间上去扩展一个元素,即区间长度为 (3),无论第三个元素的大小,中位数都一定是 (k),那么我们就得到了三个连续的 (k),以此类推下去我们就可以构造出来了。
那么我们接下来的任务就是在 (k) 的左边或右边搞出一个 (geq k) 的数,那么我们根据上面的思路,如果存在两个连续的数 (geq k),那么我们就可以扩展到三个数,四个数......直到遇到扩展的元素为 (k)。
那么我们就得到了一个条件:存在两个连续的数 (geq k);
如果不满足上述条件是不是就一定不可以了呢?对于这种情况:(a_i, a_{i+1}, a_{i+2},(a_i geq k, a_{i+1} < k, a_{i+2} geq k)),显然也是可以的。所以只要满足这两个条件就可以了。
还有一个坑点就是要特判 (n = 1) 的情况。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, k;
int a[N];
bool judge(){
int mark = 0;
for(int i = 1; i <= n; i ++){
if(a[i] == k) mark = 1;
}
if(!mark) return 0;
if(n == 1 && a[n] == k) return 1;
for(int i = 2; i <= n; i ++){
if(a[i] >= k && a[i - 1] >= k) return 1;
if(i >= 3 && a[i] >= k && a[i - 2] >= k) return 1;
}
return 0;
}
int main(){
int t; cin >> t;
while(t --){
cin >> n >> k;
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
if(judge()) puts("yes");
else puts("no");
}
return 0;
}
E. Orac and Game of Life
(Description:)
给出一个 (0, 1) 矩阵,每经过一轮循环矩阵就有可能变换。
(1.) 如果一个元素存在一个相邻元素和他相同,那么该元素就会变换。
(2.) 如果不存在,那么该轮循环就不会发生变换。
(Solution:)
显然的一个事实是:如果矩阵中不存在一组相邻的元素相同,那么矩阵就永远不会变。否则,矩阵的每一位元素都有可能发生变换,可以参考样例三。
那么我们只需要把那些可以发生变换的元素提取出来,然后用他们去同化那些原本不会变的元素即可,记录下每个元素的第一次变换的循环是那一次即可。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10, INF = 0x3f3f3f3f;
typedef pair<int, int> PII;
typedef long long ll;
int n, m, t;
char s[N][N];
int a[N][N];
int vis[N][N];
int d[N][N];
int to[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int check(int x, int y){
if(x >= 1 && x <= n && y >= 1 && y <= m) return 1;
return 0;
}
int main(){
cin >> n >> m >> t;
for(int i = 1; i <= n; i ++)
scanf("%s", s[i] + 1);
memset(vis, 0, sizeof vis);
memset(d, INF, sizeof d); // 第一次变换的循环
queue<PII> q;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j ++){
a[i][j] = s[i][j] - '0';
for(int k = 0; k < 4; k ++){
int x = i + to[k][0];
int y = j + to[k][1];
if(check(x, y) && s[x][y] == s[i][j]){
q.push({i, j}); // 提取存在相邻相同元素的
d[i][j] = 0; // 置为 0,因为原本就会变
vis[i][j] = 1; // 标记已经访问过
break;
}
}
}
while(!q.empty()){
PII t = q.front(); q.pop();
int x = t.first, y = t.second;
for(int i = 0; i < 4; i ++){ // 用这个元素去同化他周围的
int xx = x + to[i][0];
int yy = y + to[i][1];
if(check(xx, yy) && !vis[xx][yy]){
q.push({xx, yy});
d[xx][yy] = d[x][y] + 1;
vis[xx][yy] = 1; // 标记一下
}
}
}
while(t --){
int x, y;
ll p; // p 很大
scanf("%d%d%lld", &x, &y, &p);
p -= d[x][y]; // 减去需要同化的循环次数
if(p <= 0 || d[x][y] == INF) printf("%d
", a[x][y]);
else printf("%d
", a[x][y] ^ (p & 1));
}
return 0;
}