Educational Codeforces Round 74 (Rated for Div. 2)

跳过了一些困难的题开一场新的。看来2100的题还是要慢慢挑战,不能操之过急。

A - Prime Subtraction

题意:给两个数x,y,可以从x中减去任意个相同的质数p,问能不能与y相等。

题解:首先x>=y,然后差值不能为1,否则一定可以。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

void test_case() {
    ll x, y;
    scanf("%lld%lld", &x, &y);
    if(x < y || x == y + 1)
        puts("NO");
    else
        puts("YES");
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

B - Kill 'Em All

题意:x正半轴上有n个怪物,分别在xi处。每次可以发射一颗导弹,命中位置的怪物被秒杀,其他怪物会被推向两边(位移为r),被推到非负半轴的也会被秒杀。求最少的导弹。

题解:假如没有“被推到非负半轴的也会被秒杀”,那么数量就是怪物离散化坐标的数量,现在要充分利用这个条件,显然要把怪物从最右边开始推过去。记录累计的导弹数量就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int MAXN = 1e5;
int a[MAXN + 5];
 
void test_case() {
    int n, r;
    scanf("%d%d", &n, &r);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    sort(a + 1, a + 1 + n);
    n = unique(a + 1, a + 1 + n) - (a + 1);
    int suml = 0, cnt = 0;
    for(int i = n; i >= 1; --i) {
        if(a[i] > suml) {
            ++cnt;
            suml += r;
        } else
            break;
    }
    printf("%d
", cnt);
}
 
int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

C - Standard Free2play

题意:你的游戏角色站在h层高的平台,当你站在平台上时,你必须拉动开关使得当前平台及他的下一层平台的伸出/收起状态改变,然后你可以落下至多2层。你可以用水晶改变除了h层的每个平台的初始状态。求最少的水晶。

题解:看起来像dp,设dp[i]表示从h层下到第i层的需要的最小花费。转移非常显然。但是h太大了。然后显然的假如h下面一直都没有东西,角色可以一直拉动开关降到地面。但是当中间有至少一个平台时,会面临自己下方的平台x-1本身是伸出的情况,这时假如再下一层还有平台x-2就可以直接无视然后掉到x-2,否则就要花一颗水晶消除它。所以就是可以一次消灭两个连续的数字中的高的那个,然后从低的那个继续开始。简化问题可以把0层也作为一块伸出的平台。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 2e5;
int p[MAXN + 5];

void test_case() {
    int h, n;
    scanf("%d%d", &h, &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &p[i]);
    if(n == 1) {
        printf("0
");
        return;
    }
    int cnt = 0, cur = h;
    p[n + 1] = 0;
    for(int i = 2; i <= n; ++i) {
        cur = p[i] + 1;
        if(p[i] == p[i + 1] + 1) {
            cur = p[i + 1];
            ++i;
        } else
            ++cnt;
    }
    printf("%d
", cnt);
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

D - AB-string

题意:一个只由AB组成的字符串s,定义一个字符串是好的,当它每个字母都至少属于一个长度>1的回文串。求s的好的子串的数量。

题解:模拟一下题意,发现不好的长度>1串必须满足这四种形式:

AA...AB
BB...BA
ABB...B
BAA...A

分几种情况统计,第一种统计连续A的数量,遇到B的时候统计;第二种同理;第三种,统计前一个字母是A的连续B的数量,在这个连续B结束(遇到A/遇到字符串结尾)时统计,第四种同理。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 3e5;
char s[MAXN + 5];
ll dp[MAXN + 5][4];

void test_case() {
    int n;
    scanf("%d%s", &n, s + 1);
    if(n == 1) {
        puts("0");
        return;
    }
    ll cnt = 0;
    dp[1][0] = (s[1] == 'A');
    dp[1][1] = (s[1] == 'B');
    dp[1][2] = 0, dp[1][3] = 0;
    for(int i = 2; i <= n; ++i) {
        if(s[i] == 'A') {
            dp[i][0] = dp[i - 1][0] + 1;
            dp[i][1] = 0;
            dp[i][2] = dp[i - 1][2] ? dp[i - 1][2] + 1 : s[i - 1] != s[i];
            dp[i][3] = 0;
            cnt += dp[i - 1][1];
            cnt += dp[i - 1][3] - (dp[i - 1][3] >= 1);
        } else {
            dp[i][0] = 0;
            dp[i][1] = dp[i - 1][1] + 1;
            dp[i][2] = 0;
            dp[i][3] = dp[i - 1][3] ? dp[i - 1][3] + 1 : s[i - 1] != s[i];
            cnt += dp[i - 1][0];
            cnt += dp[i - 1][2] - (dp[i - 1][2] >= 1);
        }
        //printf("i=%d
 cnt=%lld
",i,cnt);
    }
    cnt += dp[n][2] - (dp[n][2] >= 1);
    cnt += dp[n][3] - (dp[n][3] >= 1);
    printf("%lld
", 1ll * n * (n - 1) / 2 - cnt);
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    //scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

E - Keyboard Purchase

题意:你要输入一串n长包含前m个小写英文字母的密码,你要购买一种所有键是m种字母的一个排列的直线的键盘,手指移动的距离就是相邻两个字母的键盘距离的总和。求最小的手指移动距离。

题解:看见m<=20,假如m<=6岂不是枚举全排列然后暴力验证?可惜改不得。但是这个数据量暗示什么?暗示可以状压,于是口胡一种做法。dp[i][j]表示把状态j中选出的字母全部排在左边,顺序任意,解决前i位密码的最短距离?忽视了一点,这个是有后效性的,前面字母排列的顺序是会影响后面的手指距离的。不懂怎么写,看题解。

题解也是状压dp,但是是逐个字母加进来的而不是与密码的顺序有关,首先统计出每两个字母的相邻的对数,然后密码就没用了。设dp[j]为把状态j中选出的字母全部排在左边,顺序任意,形成的最短距离。显然的dp[0]=0。考虑加入一种字母x,遍历

参考资料:

Educational Codeforces Round 74 Editorial - Codeforces
Codeforces 1238E. Keyboard Purchase - LLTYYC - 博客园

原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11870120.html