Codeforces Round #641 (Div. 2) 做题记录

A:Orac and Factors

题意:看题面吧,水题

题解:一次操作后就会有因子2,直接做就完了。

参考代码:

#include 

typedef long long ll;
using namespace std;

const int maxn = 2e6 + 7;
int f[maxn];
int main(){
#ifndef ONLINE_JUDGE
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    memset(f, 0x3f, sizeof(f));
    f[1] = 1;
    for(int i = 2; i < maxn; ++i) {
        for(int j = i; j < maxn; j += i) f[j] = min(f[j], i);
    }
    int t;
    scanf("%d", &t);
    while(t--) {
        ll n, k;
        scanf("%I64d%I64d", &n, &k);
        while(k > 0 && f[n] > 2) {
            n += f[n];
            --k;
        }
        if(k) n += k * 2;
        printf("%I64d\n", n);
    }
    return 0;
}

 

B:Orac and Models

题意:给定一个长度为n的序列,求满足条件的最长上升子序列长度:序列中相邻元素下标大的能被下标小的整除。

题解:直接将下标因式分解,做dp。

参考代码:

#include 
#define eb emplace_back

typedef long long ll;
using namespace std;

const int maxn = 1e5 + 7;
vector yz[maxn];
int n, f[maxn], a[maxn];
int main(){
#ifndef ONLINE_JUDGE
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    int t;
    for(int i = 1; i < maxn; ++i) {
        for(int j = i; j < maxn; j += i) {
            yz[j].eb(i);
        }
    }
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        int ans = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            f[i] = 1;
            for(int p : yz[i]) {
                if(a[p] < a[i]) f[i] = max(f[i], f[p] + 1);
            }
            ans = max(ans, f[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

 

C:Orac and LCM

题意:给一个长度为n序列a,对a序列中的每对数求lcm得到序列b,求序列bgcd

数据范围:2 <= n <= 10^5, 1 <= a_i <=2 * 10 ^ 5

题解:对于一对数,它们的lcm就是将它们素因子分解后,对应的素因子指数取最大值得到的结果。由此,我们先对序列a中的数进行素因子分解并将每个素因子的次数记录到对应的桶中。

对于一个素因子p

第一种情况:如果每个数都有素因子p,那么其对结果的贡献由其第二小的出现次数决定。

第二种情况:如果只有1个数没有素因子p,那么其对结果的贡献由第一小的出现次数决定。

第三种情况:如果有2个以上的数没有素因子p,那么其对结果无贡献。

具体计算方法见代码实现。

参考代码:

#include 

typedef long long ll;

using namespace std;

const int maxn = 2e5 + 7;
int n, x;
vector p[maxn];

ll quick_pow(ll x, ll y) {
   // printf("x y --- %lld %lld\n", x, y);
    ll res = 1;
    for(; y > 0; x = x * x, y >>= 1) if(y & 1) res = res * x;
    return res;
}
int main(){
#ifndef ONLINE_JUDGE
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &x);
        for(int j = 2; 1LL * j * j <= x; ++j) {
            if(x % j == 0) {
                int cnt = 0;
                while(x % j == 0) ++cnt, x /= j;
                p[j].push_back(cnt);
            }
        }
        if(x > 1) p[x].push_back(1);
    }
    ll ans = 1;
    for(int i = 2; i < maxn; ++i) {
        sort(p[i].begin(), p[i].end());
        if(p[i].size() >= n) {
            ans = ans * quick_pow(i, p[i][1]);
        }
        else if(p[i].size() == n - 1) {
            ans = ans * quick_pow(i, p[i][0]);
        }
    }
    printf("%I64d\n", ans);
    return 0;
}

D:Orac and Medians

题意:给定一个长度为n的 序列a,定义一次操作为选定序列中的一个区间[l, r],将此区间中的数全都变为此区间的数排序后第(r - l + 1 + 1) / 2的数,可以进行无限次操作,问能不能通过操作将所有数都变成k

数据范围:1 <= n <= 10 ^ 5, 1 <= a_i, k <= 10 ^ 9

题解:首先,要使所有数都变成k,序列中必须有k。有k了之后分情况讨论一下:

第一种情况:存在相邻两个>=k的数,那么一定能够通过操作使序列全变成k

第二种情况:不存在相邻两个>=k的数,我们就找所有长度为3的区间,看能不能变出一个长度为3的>=k的区间且其他位置还有k或者当前的中位数是k,如果能,则可以将序列都变成k,否则不可以。

参考代码:

#include 
#define pb push_back
#define eb emplace_back
#define sz(x) (int)(x).size()

typedef long long ll;
using namespace std;

const int maxn = 1e5 + 7;
int n, k, a[maxn], preh[maxn], sufh[maxn];
int main(){
#ifndef ONLINE_JUDGE
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &k);
        vector kpos;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            preh[i] = preh[i - 1];
            if(a[i] == k) kpos.eb(i), preh[i] = 1;
        }
        sufh[n + 1] = 0;
        for(int i = n; i >= 1; --i) {
            sufh[i] = sufh[i + 1];
            if(a[i] == k) sufh[i] = 1;
        }
        if(sz(kpos) == 0) {
            puts("no");
            continue;
        }
        if(n == sz(kpos)) {
            puts("yes");
            continue;
        }
        bool flag = false;
        for(int i = 1; i < n; ++i) {
            if(a[i] >= k && a[i + 1] >= k) flag = true;
        }
        if(flag) {
            puts("yes");
            continue;
        }
        for(int i = 2; i <= n - 1; ++i) {
            vector b;
            for(int j = i - 1; j <= i + 1; ++j) b.eb(a[j]);
            sort(b.begin(), b.end());
            if(b[1] == k) flag = true;
            if(b[1] > k && (preh[i - 2] || sufh[i + 2])) flag = true;
        }
        puts(flag ? "yes" : "no");
    }
    return 0;
}

 

E:Orac and Game of Life

题意:给一个nm列的01矩阵,每一轮每个格子会发生如下变化:

如果一个格子与其相邻格子没有一个是相同的,那么这个格子本轮不变。

否则,这个格子就会01反转。

t个询问,每次询问ij列的元素在第p轮是什么数字。

数据范围:1 <= n, m <= 1000, 1 <= t <= 10 ^ 5

1 <= i <= n, 1 <= j <= m, 1 <= p <= 10 ^ 1 ^ 8

题解:首先对于一个会反转的点,它反转以后的每一轮都会反转。对于第1轮就会反转的点将其加入起点,直接bfs求出每个点在第几轮后会跟着反转。询问时直接查询即可。

参考代码:

#include 

typedef long long ll;
typedef std::pair pii;
using namespace std;

const int maxn = 1000 + 7, inf = 0x3f3f3f3f;

char mp[maxn][maxn];
int f[maxn][maxn];
int n, m, t;
int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};

bool check(int x, int y) {
    if(x < 1 || x > n || y < 1 || y > m) return false;
    return true;
}
int main(){
#ifndef ONLINE_JUDGE
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    scanf("%d%d%d", &n, &m, &t);
    memset(f, 0x3f, sizeof(f));
    for(int i = 1; i <= n; ++i) {
        scanf("%s", mp[i] + 1);
    }
    queue Q;
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= m; ++j) {
            bool flag = false;
            for(int k = 0; k < 4; ++k) {
                int tx = i + dir[k][0], ty = j + dir[k][1];
                if(check(tx, ty)) {
                    if(mp[tx][ty] == mp[i][j]) flag = true;
                }
            }
            if(flag){
                f[i][j] = 0;
                Q.push(pii(i, j));
            }
        }
    }
    while(!Q.empty()) {
        pii p = Q.front(); Q.pop();
        for(int k = 0; k < 4; ++k) {
            int tx = p.first + dir[k][0], ty = p.second + dir[k][1];
            if(check(tx, ty)) {
                if(f[tx][ty] == inf) {
                    f[tx][ty] = f[p.first][p.second] + 1;
                    Q.push(pii(tx, ty));
                }
            }
        }
    }
    for(int q = 1; q <= t; ++q) {
        int i, j;
        ll p;
        scanf("%d%d%I64d", &i, &j, &p);
        int res = mp[i][j] - '0';
        if(f[i][j] != inf && p > f[i][j]) {
            p -= f[i][j];
            p &= 1;
            res ^= p;
        }
        printf("%d\n", res);
    }
    return 0;
}

F:Slime and Sequences (Easy Version)

题意:对于长度为n的序列p,定义好序列为:

对于每个出现在序列中的大于1的数k,至少存在一对i, j(1<=i < j <= n)满足p_i = k -1, p_j = k

给定序列的长度n,求所有长度为n的好序列中1-n每个数字的出现次数和。

数据范围:简单版(1 <= n <= 5000), 困难版(1 <= n <= 10 ^ 5)

题解:留坑

你可能感兴趣的:(Codeforces Round #641 (Div. 2) 做题记录)