Codeforces #641 div2 A - E 解题报告

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),\ lcm(a_1,a_3),\ ...,\ lcm(a_1,a_n)) = lcm(a_1,\ gcd(a_2,\ a_3,...,\ a_n)) \]

  我们预处理出一个 \(gcd\) 的后缀,就可以简单的得出结果。

  证明一下上面的公式:

    假设:\(lcm(a_1, a_2) = a_1 \times a_2 \times x_2\),那么 \(x_2 = \frac{1}{gcd(a_1,a_2)}\);

    同理:\(lcm(a_1,a_3) = a_1 \times a_3 \times 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 \times a_2 \times x_2, a_1 \times a_3 \times x_3) = a_1 \times gcd(a_2 \times x_2, a_3 \times x_3)\)

    因为:\(gcd(ab, m) = gcd(a, m) \times gcd(b, m)\);

    所以:\(t = a_1 \times gcd(a_2, a_3) \times gcd(x_2, x_3) \times gcd(a_2,x_3) \times 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 \times gcd(a_2,a_3) \times gcd(x_2, x_3) = \frac{a_1 \times 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 \times 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\n", a[x][y]);
        else printf("%d\n", a[x][y] ^ (p & 1));
    }

    return 0;
}
posted @ 2020-05-13 20:37  nonameless  阅读(266)  评论(2编辑  收藏  举报