-测验复盘

C - Sending Messages

 CodeForces - 1921C 

题目描述 

斯蒂潘是一个非常忙碌的人。今天他需要在时刻m1,m2,…mn(mi<mi+1)发送n条消息。不幸的是,到时刻00时,他的手机只剩下f单位的电量。在时刻00,手机是开着的。

手机每开启一段时间就会消耗a单位的电量。此外,斯蒂潘随时可以关闭手机,然后稍后再开启。每次这样做会消耗b单位的能量。考虑到开关机是瞬间完成的,所以你可以在时刻x开启手机并发送一条消息,反之亦然,可以在时刻x发送消息并关闭手机。

如果在任何时刻电量下降到0(变成≤0),那么在该时刻发送消息是不可能的。

由于所有消息对斯蒂潘来说都非常重要,他想知道是否可以在不充电的情况下发送所有消息。

输入

输入的第一行包含一个整数t(1≤t≤1e4)— 测试用例的数量。接下来是每个测试用例的描述。

每个测试用例的第一行包含四个整数n、f、a和b(1≤n≤2⋅10^{5}、1≤f,a,b≤10^{9})— 消息数量、初始手机电量、每单位时间的电量消耗,以及连续开关机的消耗。

每个测试用例的第二行包含n个整数m1,m2,…,mn,(1≤mi≤10^{9}、mi<mi+1)— 需要发送消息的时刻。

保证在一个测试用例中,所有测试用例的n之和不超过2⋅10^{5}

输出

对于每个测试用例,如果斯蒂潘可以发送所有消息,则输出“YES”,否则输出“NO”。

你可以以任何大小写形式输出每个字母(小写或大写)。例如,字符串“yEs”、“yes”、“Yes”和“YES”都将被接受为肯定的答案。

示例 

InputcopyOutputcopy
6
1 3 1 5
3
7 21 1 3
4 6 10 13 17 20 26
5 10 1 2
1 2 3 4 5
1 1000000000 1000000000 1000000000
1000000000
3 11 9 6
6 8 10
12 621526648 2585904 3566299
51789 61859 71998 73401 247675 298086 606959 663464 735972 806043 806459 919683
NO
YES
YES
NO
NO
YES
#include <stdio.h>

#define ll long long

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        ll n, f, a, b;
        ll temp = 0;
        scanf("%lld %lld %lld %lld", &n, &f, &a, &b);
        ll x = 0, y = 0; //前后两个数
        for (ll i = 0; i < n; i++) {
            scanf("%lld", &y); //读取后一个数
            if ((y - x) * a < b) {
                 temp += (y - x) * a; 
                 } 
            else {
                 temp += b; 
                 }
            x = y;//提至前一位
        }
        if (temp < f) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

题目分析

    普通的贪心问题,对于每个样例,仅需考虑每个时刻与上个时刻的间隔待机所消耗的电量是否大于关机所消耗的电量即可,将大问题分解为多个子问题进行求解;

    (测验时脑子抽了数据没处理好开的int还存了临时数据) --有大开大,少整别的


代码示例


D - Infinite Replacement

 CodeForces - 1674C 

给一个只含小写字母 a 的字符串 s 和一个用来替换的字符串 t 。

你可以将 s 中任意一个字母 a 用 t 来替换,替换的次数不限。

对于每一个 s 和 t ,你可以得到几个不同的字符串?如果有无限个,输出 -1 。

输入输出样例

输入                                                                                输出 

3                                                1
aaaa                                            -1
a                                                2
aa
abc
a
b

题目分析

1.若t中有字母“a”,则能一直进行嵌套。如果t本身等于“a”,则字符串不会改变,故答案是1;反之,当t不等于原字符串,字符串s每次移动后长度都会增加且不相同,故答案是-1。

2除去以上两种情况.每一个 s 中的a便有换成 t 或不换两种可能性,又因为 s 全为字符a组成,所以根据乘法原理易得答案为2^{n}。(注意数据范围)


代码示例

#include <stdio.h>
#include <string.h>
#include <math.h>

int main() {
    int a;
    scanf("%d", &a);
    for (int i = 0; i < a; i++) {
        char s[100], t[100];
        scanf("%s", s);
        scanf("%s", t);
        if (!strcmp(t, "a")) { //判断t是否等于"a"
            printf("1\n");
        }
        else if (strstr(t, "a")) { //查找t是否含有"a"
            printf("-1\n");
        }
        else {
            printf("%lld\n", (long long)pow(2, strlen(s))); //开long long,不然会炸
        }
    }
    return 0;
}

E - Also Try Minecraft

 CodeForces - 1709B 

题目描述

有一个 1 到 n 的序列 a,ai​ 代表第 i 个格子的高度,若你在 x,则你可以走到 x+1 或 x−1。若你从一个高为 p 的格子走到一个比它矮的高为 q 格子会受到p−q 的伤害,若走到一个比它高的格子则不会受到伤害。共 m 组询问,每组询问给出 s 和 t,你要求从 s 走到 t 所受到的最少的伤害。


输入

输入的第一行包含两个整数n和m(2≤n≤10^{5};1≤m≤10^{5})— 列数和你需要测试的任务数。

输入的第二行包含n个整数a1,a2,…,an(1≤ai≤10^{9}),其中ai是第i列的高度。

接下来的m行描述了任务。第j个任务包含两个整数sj和t(1≤sj,tj≤n;sj≠tj),表示你需要在第j个任务期间从第sj列移动到第tj列。

注意sj可能大于tj。

输出

输出m个整数。其中第j个整数应该是在第j个任务完成时你可以受到的最小坠落伤害。

示例 

InputcopyOutputcopy
7 6
10 8 9 6 8 12 7
1 2
1 7
4 6
7 1
3 5
4 2
2
10
0
7
3
1

题目分析 

     题做少了不会看数据范围,测验的时候当模拟题直接跑结果tl了,现在转头来看这题显然是一道前缀和的模板题


代码示例

#include <stdio.h>
#include <math.h>

#define ll long long

const int N = 2e5;

ll a[N];
ll d1[N] = {0}, d2[N] = {0};//d1左往右,d2右往左
ll n, m;


int main () {
    scanf("%lld %lld", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
    }
    for(int i = 2; i <= n; i++) {
        if(a[i - 1] > a[i]) d1[i] = d1[i - 1] + a[i - 1] - a[i];
        else d1[i] = d1[i - 1];
    }
    for(int i = n - 1; i >= 1; i--) {
        if(a[i + 1] > a[i]) d2[i] = d2[i + 1] + a[i + 1] - a[i];
        else d2[i] = d2[i + 1];
    }
    while(m--) {
        ll st, ed;
        scanf("%lld %lld", &st, &ed);
        if(st > ed) printf("%lld\n", d2[ed] - d2[st]);
        else printf("%lld\n", d1[ed] - d1[st]);
    }
    return 0;
}

F - Summation Game

 CodeForces - 1920B 

题目描述

给定一个序列 1,2,…,a1​,a2​,…,an​,首先 A 先选择至多 k 个数删除,然后 B 在剩下的数中选择至多 x 个数乘上−1,加上剩余的数后,A 想要和尽可能大,B 想要和尽可能小。若两者都通过最佳决策,求结果为多少。


输入

每个测试包括多个测试用例。第一行包含一个整数 t (1≤t≤10^{4}) — 测试用例的数量。接下来是测试用例的描述。

每个测试用例的第一行包含三个整数 n, k, 和 x (1≤n≤2⋅10^{5}, 1≤x,k≤n) — 数组中的元素数量,A可以移除的元素数量限制,以及B可以乘以 −1−1 的元素数量限制。

每个测试用例的第二行包含 n 个整数 a1,a2,…,an(1≤ai≤1000) — 数组的元素。

保证所有测试用例中 n的总和不超过 2⋅10^{5}

输出

对于每个测试用例,输出一个整数 — 假设两位玩家都采取最优策略后,游戏结束后数组元素的总和。

示例 

InputcopyOutputcopy
8
1 1 1
1
4 1 1
3 1 2 4
6 6 3
1 4 3 2 5 6
6 6 1
3 7 3 3 32 15
8 5 3
5 5 3 3 3 2 9 9
10 6 4
1 8 2 9 3 3 4 5 3 200
2 2 1
4 3
2 1 2
1 3
0
2
0
3
-5
-9
0
-1

 题目分析 

        难点就在分析。   对于A来说,并非删的越多越好,但对于B而言, 一定是每次尽可能选择最大的数,从而才能使结果最小化;而A只能阻止B选择大的数字。得,分析出来了,贪心题。

具体步骤

关于A删除的数字个数 i 进行枚举,再维护删除后的总和和 B取反数字的总和

(然后兴冲冲写完跑去交,得,TL)注意求和时使用前缀和优化。


代码示例

#include <stdio.h>
#include <stdlib.h>


const int N = 2e5 + 10;

int a[N] = {0}, p[N] = {0};


void merge_sort(int *arr, int l, int r) {
    if(r - l <= 1) return;
    int mid = (l + r) / 2;
    merge_sort(arr, l, mid);
    merge_sort(arr, mid, r);
    int p1 = l, p2 = mid, k = 0;
    int *temp = (int *)malloc(sizeof(int) * (r - l));
    while(p1 < mid || p2 < r) {
        if(p2 == r || (p1 < mid && arr[p1] >= arr[p2])) {
            temp[k++] = arr[p1++];
        } else {
            temp[k++] = arr[p2++];
        }
    }
    for(int i = l; i < r; i++) arr[i] = temp[i - l];
    free(temp);
    return ;
}

int main() {
    int T;
    int n, k,x;
    scanf("%d", &T);
    while(T--) {
        int maxn = -1e9;
        scanf("%d %d %d", &n, &k, &x);
        for(int i = 1; i <= n; i++){
            scanf("%d", &a[i]);
        }
        merge_sort(a, 1, n + 1);//保证升序排布便于查找
        for(int i = 1; i <= n; i++) p[i] = p[i - 1] + a[i];
       	for (int i = 0; i <= k; i ++){
            int res = 0;
            int temp = (i + x) > n ? n : (i + x);
            res = p[n] - 2 * p[temp] + p[i];
            maxn = maxn > res ? maxn : res;
       	}
       	printf("%d\n", maxn);
    }
    return 0;
}


  • 46
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值