Educational Codeforces Round 74 A~F

本文解析了 EducationalCodeforces Round 74 中的五道题目:A. 质数减法判断、B. 消灭怪兽的最优炮击策略、C. 高台调整的最小操作次数、D. AB 字符串的好子串计算、E. 键盘布局最小移动次数。展示了不同领域的算法应用,如质因数分解、贪心算法、动态规划和状态压缩。
摘要由CSDN通过智能技术生成

Educational Codeforces Round 74 A~F

A. Prime Subtraction(唯一分解)

链接
题意:给出两个数,问x可不可以通过减去质数得到y
思路:唯一分解定理

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
    int T;
    cin >> T;
    while (T--) {
        int a, b;
        cin >> a >> b;
        if (a - 1 != b) {cout << "YES" << endl;}
        else cout << "NO" << endl;
    }
    return 0;
}

B. Kill 'Em All(贪心)

链接
题意:一个水平的坐标轴,怪兽都在原点右边,若使得怪物到达原点右边,则怪兽死亡。可以开炮
①如果怪物在炮弹上,怪物死亡
②怪物在炮弹左边,怪物左移给定的r个单位
③怪物在炮弹右边,怪物右移给定的r个单位
当怪物在原点左边(包括原点)也算死亡
思路:贪心的去看,我们发现怪物在右边是无效操作,所以我们从最远的怪物开始打,一直往前打到怪物全死就行

#include<bits/stdc++.h>
using namespace std;
#define int long long
int st[100010];
signed main()
{
    int T;
    cin >> T;
    while (T--) {
        int n, r;
        cin >> n >> r;
        vector<int> vec;
        for (int i = 1; i <= n; i++) {
            int x;
            cin >> x;
            if (!st[x]) vec.push_back(x);
            st[x] = 1;
        }
        sort(vec.begin(), vec.end(), greater<int>());
        int ans = 0;
        for (int i = 0; i < vec.size(); i++) {
            if (vec[i] <= ans * r) break;
            ans++;
        }
        cout << ans << endl;
        for (int i = 0; i < vec.size(); i++) st[vec[i]] = 0;
    }
    return 0;
}

C. Standard Free2play(dp)

链接
题意:右一些连续的高台 1 → 1 0 18 1 \to 10^{18} 11018,现在你在最高处 h [ 1 ] h[1] h[1],你的任务是到1,现在只有一部分高台可以站人,但是你每次落下的时候,会把下一个高台变成相反的状态,你落脚的地方不能和你下落的地方落差超过2,现在你要改变一些高台的初始状态,让你可以安全的到1,问最少操作多少次
思路:只用看这一个的上一个和上上个是不是差一就行了。。差一就要从上一个花费一个代价,不然直接就从上上个转移就行了。。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int h[N], dp[N];
int main()
{
    int T;
    cin >> T;
    while (T--) {
        int st, n;
        cin >> st >> n;
        for (int i = 1; i <= n; i++) {
            cin >> h[i]; h[i + 1] = 0;
            dp[i] = 0;
        }
        for (int i = n - 1; i >= 1; i--) {
            if (h[i + 2] + 1 != h[i + 1]) dp[i] = dp[i + 1] + 1;
            else dp[i] = dp[i + 2];
        }
        cout << dp[1] << endl;
    }

    return 0;
}

D. AB-string(思维)

链接
题意:给出字符串,好子串的定义是,子串每一个字符都包含在这个子串的一个回文子串中,问有多少个好子串,这个子串只由AB构成
思路:我们可以发现,其实只要有2个相同的字符,他们中间字符的就都是在好子串中比如 A . . . . . . A A......A A......A,特别的 B A A A A A BAAAAA BAAAAA这种不是好子串,所以我们只需要记录上一个A和上一个B的位置就可以了,现在是A,则从上一个A开始往前的子串全是好子串,并且上一个B在上一个A前面的时候要减1,上面已经讨论过了,B同理

#include<bits/stdc++.h>
using namespace std;
#define int long long
char s[300010];
signed main()
{
    int n;
    cin >> n;
    cin >> (s + 1);
    int a = 0, lasta = 0, b = 0, lastb = 0, ans = 0;
    for (int i = 1; i <= n; i++) {
        if (s[i] == 'A') {
            ans += lasta;
            if (lastb < lasta && lastb) ans--;
            lasta = i;

        }
        if (s[i] == 'B') {
            ans += lastb;
            if (lasta < lastb && lasta) ans--;
            lastb = i;

        }
    }cout << ans << endl;
    return 0;
}

E. Keyboard Purchase(状态压缩dp)

链接
题意:给出目标字符串,你要设计一个键盘,他的字母排成一排,要求最少需要在键盘移动多少次可以输入这个字符串
思路:我们可以状压键盘,所以我们转移的时候需要的东西就是每新加入一个字母的时候他的贡献,我们统计相邻的字母记为 c n t [ i ] [ j ] cnt[i][j] cnt[i][j],新加入的代价就是 c n t [ i ] [ j ] ∗ ( p o s i − p o s j ) = c n t [ i ] [ j ] ∗ p o s i − c n t [ j ] [ i ] ∗ p o s j cnt[i][j] * (pos_i-pos_j)=cnt[i][j]*pos_i-cnt[j][i]*pos_j cnt[i][j](posiposj)=cnt[i][j]posicnt[j][i]posj我们把 c n t [ i ] [ j ] ∗ p o s i cnt[i][j]*pos_i cnt[i][j]posi看成 t m p tmp tmp,所以我们对于每一个代价,直接加上他的 t m p tmp tmp,就行了,对于已经加入键盘的,我们的 t m p tmp tmp就是 c n t [ i ] [ j ] ∗ n u m cnt[i][j]*num cnt[i][j]num,否则就减去tmp,num是已经加入键盘的字母的个数,他多余的会抵消掉

#include<bits/stdc++.h>
using namespace std;
//#define int long long
int dp[2000010], cnt[30][30];
int main()
{
    int n, m;
    cin >> n >> m;
    string s;
    cin >> s;
    for (int i = 0; i < n - 1; i++) {
        cnt[s[i] - 'a'][s[i + 1] - 'a']++, cnt[s[i + 1] - 'a'][s[i] - 'a']++;
    }
    for (int i = 0; i < m; i++) cnt[i][i] = 0;
    int len = (1 << m) - 1;
    memset(dp, 0x3f, sizeof (dp));dp[0] = 0;
    for (int i = 0; i <= len; i++) {
        int num = 0;
        for (int j = 0; j < m; j++) if ((1 << j) & i) num++;
        for (int j = 0; j < m; j++) {
            if (((1 << j) & i) == 0) {
                int sum = 0;
                for (int k = 0; k < m; k++) {
                   if((1 << k) & i)sum += cnt[j][k] * num;
                    else sum -= cnt[k][j] * num;
                }
                dp[i | (1 << j)] = min(dp[i] + sum, dp[i | (1 << j)]);
            }
        }
    }
    cout << dp[len];
    return 0;
}

F. The Maximum Subtree(树的直径)

链接
题意:给出一颗树,你要给每个点设置 [ l , r ] [l,r] [l,r],好树的定义是如果两个节点之间的区间可以包含,就在他们之间链接一条边,最后和原树子树一样,问最大的好树有多大
思路:可以发现,除了树最左边和最右边的点,其他的点不可以存在儿子节点,因为这样就会和上面的区间形成交集,破坏子树,所以我们只需要求一个树的直径就行了,然后统计直径链最大的点的度数和就行了

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
int head[N], idx;
struct Edge{int to, nxt;}e[N << 1];
void add(int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
int du[N];
int st, ans, n;
void dfs(int u, int fa, int d)
{
    if (d > ans) {ans = d, st = u;}
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v == fa) continue;
        dfs(v, u, d + du[v]);
    }
}

int main()
{
    int T;
    cin >> T;
    while (T--) {
        cin >> n;
        for (int i = 1; i <= n; i++) head[i] = 0, du[i] = -1; idx = 0;
        for (int i = 1; i < n; i++) {
            int u, v;
            cin >> u >> v;
            add(u, v); add(v, u);
            du[u]++, du[v]++;
        }
        ans = -1;
        dfs(1, -1, du[1]);
        dfs(st, -1, du[st]);
        cout << ans + 2 << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值