【cf补题记录】Codeforces Round #614 (Div. 2)

比赛传送门

这场感觉挺简单...因为我会做D题......



A

题意:有n层楼,有个人在第s层,其中,每层楼都有一个吃饭的地方,但现在有k层楼是不能吃饭。问这个人从第s层出发,至少要走多少层才能吃到饭。

题解:因为n的范围是2~1e9,所以不能建一个bool值表O(1)的速度判断第i层是否可以用餐;另外k的范围是1 ~ min(n-1, 1000),即k的最大值是1000;所以即便第s层在这k层里的中间位置,向低层或者高层延申最多也只需要走(总共)1000层,同时,因为要判断该层是否可以用餐需要查询该层是否包含在这k层里,所以时间复杂度是O(\(k^2\)),即1e6——不用优化就可以过了。

// https://codeforces.com/contest/1293/problem/A
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int T, n, s, k;
int a[1003];

int main()
{
    scanf("%d", &T);
    while(T--){
        scanf("%d %d %d", &n, &s, &k);
        for(int i = 0; i < k; i++) scanf("%d", &a[i]);

        // O(k^2)
        int l, r, i, ans = 0;
        l = r = s;
        while(true){
            if(l > 0){ // 向底层走
                for(i = 0; i < k; i++){
                    if(a[i] == l) {
                        l--;
                        break;
                    }
                }
                if(i == k) break; // 若 i == k, 则第i层可以用餐
            }

            if(r <= n){ // 往高层走
                for(i = 0; i < k; i++){
                    if(a[i] == r){
                        r++;
                        break;
                    }
                }
                if(i == k) break; // 同上
            }
            ans++;
        }

        printf("%d\n", ans);
    }
    return 0;


B

题意:有n个人参加比赛,其中有个bug选手JOE每道题都会答对,而且每当有t人(一次机会)答错出局时,选手JOE就会得到 \(\frac{t}{s}\) 的奖金(s代表的是当前还在答题的选手(不包括JOE))——即原本有n人答题,第一次淘汰了\(t_1\)人,第二次淘汰了\(t_2\)人,第三次淘汰了\(t_3\)人,那么,第三次得到的奖金是 \(\frac{t_3}{n - t_1 - t_2}\) ——问,选手JOE能在这次答题中最多能获得多少奖金。

题解:作一个简单证明:

    假设每次淘汰一个人获得的奖金比每次淘汰两个人获得的奖金多,数学式子表示如下:

    \(\frac{1}{n} + \frac{1}{n - 1} > \frac{2}{n}\)

    \(\frac{2n-1}{n(n-1)} > \frac{2n-2}{n(n-1)}\)

    \(\frac{2n-1}{n(n-1)} > \frac{2n-1}{n(n-1)} - \frac{1}{n(n-1)}\)

    因为n>0,所以以上每个分数都是正数,所以原式\(\frac{1}{n} + \frac{1}{n - 1} > \frac{2}{n}\)成立。

   所以该题的答案是 \(\sum^{n}_{i=1} \frac{1}{i}\) ,最后注意输出的是12位小数。

// https://codeforces.com/contest/1293/problem/B
#include<iostream>
#include<cstdio>
using namespace std;

int n;

int main()
{
    scanf("%d", &n);
    double ans = 0;
    for(int i = n; i > 0; i--){
        ans += 1.0 / i;
    }
    printf("%.12f\n", ans);
    return 0;
}


C

题意:有一个2 * n大小的空间,女孩如果能从从(1, 1)逃到(2, n)处即算成功逃脱,但,空间每次会改变一块(x, y)处的状态——起始时,2 * n都是安全的地,当安全地被选中时,它会变成岩浆地(不能通过此地),当岩浆地再次被选中时,它会再次变成安全地——问,女孩每次在砖块改变后,是否能逃出去(小女孩的体力与花费的时间不计入其中)。

题解:通过画图可以知道,只有上(下)相邻的砖块都变成岩浆地时,女孩才不能逃出去,即:

(情况4,5,6就是把情况1,2,3倒过来)

》》》而且,题目只需要判断女孩能不能逃出去,所以只需要判断空间是否有以上6种情况之一。因此,可以把情况量化,记录这种情况的次数,当次数为0时,即可逃出去。因为这里的n的范围是2 ~ 1e5,可以用bool值表示该块是否为安全地。

// https://codeforces.com/contest/1293/problem/C
#include<iostream>
#include<cstdio>
using namespace std;

int n, q, r, c;
bool maze[5][100005];
int stop;

int main()
{
    scanf("%d %d", &n, &q);
    while(q--){
        scanf("%d %d", &r, &c);

        bool flag = false; // 标记是否进行了消除
        if(maze[r][c]) {
            maze[r][c] = false;
            flag = true;
        }
        else maze[r][c] = true;

        if(!flag){ // 只有当有岩浆地产生时,才有可能堵住
            if(r == 1){ // 区别了上下层
                if(maze[2][c + 1]) stop += 2;
                if(maze[2][c - 1]) stop += 2;
                if(maze[2][c]) stop += 2;
            }
            else {
                if(maze[1][c + 1]) stop += 2;
                if(maze[1][c - 1]) stop += 2;
                if(maze[1][c]) stop += 2;
            }
        }
        else {
            if(r == 1){
                if(maze[2][c - 1]) stop -= 2;
                if(maze[2][c + 1]) stop -= 2;
                if(maze[2][c]) stop -= 2;
            }
            else {
                if(maze[1][c - 1]) stop -= 2;
                if(maze[1][c + 1]) stop -= 2;
                if(maze[1][c]) stop -= 2;
            }
        }

        if(stop) printf("No\n");
        else printf("Yes\n");
    }
    return 0;
}


D

题意:偶像A在(xs, ys)处,她在坐标轴上能活动t秒,每秒她只能往上下左右方向移动一次(不能斜着走)。她的目标去去到目的地。目的地的生成条件是:第0个目的地在 \((x_0, y_0)\),第i个目的地在\((a_x · x_{i-1} + b_x,\ a_y · y_{i-1} + b_y)\) ,其中\(x_{i-1},\ y_{i-1}\) 指的是第i-1个目的地位置的(x,y)。问,偶像A最多能到多少个目的地。

题解:假如偶像A能到达其中一个目的地,那么,偶像A要走到下一个目的地的时候,肯定走的是两地的最短距离,所以可以先算出目的地两两之间的距离,然后让偶像A依次以每个能到达的第i个目的地作为起点,走去下一个目的地。因为数据很大的缘故,但即便数据很大,在1e18范围内目的地也不会很多(可以在自己跑一下),所以这题的难点其实在处理数据的溢出上——因为第i个目的地的(x,y)只会比第i-1个目的地的(x,y)要大,所以如果第i个目的地比第i-1个的(x,y)要小时,就发生了溢出;另外,如果第i个目的地的(x,y)已经达到了1e18这么大的级别时,也是不能取的,因为t的最大值只能取1e16,否则容易造成后面算总和的时候的溢出。

(这题让我见识了数据溢出的可怕性了)

// https://codeforces.com/contest/1293/problem/D
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;

const LL INF = 1e18;
LL x0, y0, ax, ay, bx, by, xs, ys, t;
struct node{
    LL x, y;
}pla[1003];
LL cost[1003];

LL labs(LL a, LL b){
    if(a > b) return a - b;
    else return b - a;
}

int main()
{
    scanf("%I64d %I64d %I64d %I64d %I64d %I64d", &x0, &y0, &ax, &ay, &bx, &by);
    scanf("%I64d %I64d %I64d", &xs, &ys, &t);

    int cnt = 1;
    pla[0].x = x0; pla[0].y = y0;
    while(true){  // 生成目标点
        LL nx = ax * pla[cnt - 1].x + bx;
        LL ny = ay * pla[cnt - 1].y + by;
        // 两个判断
        if(nx > INF || ny > INF || nx < pla[cnt - 1].x || ny < pla[cnt - 1].y) break;
        pla[cnt].x = nx; pla[cnt].y = ny;
        cnt++;
    }

    // 求前序列和
    cost[0] = 0;
    for(int i = 1; i <= cnt; i++){
        cost[i] = cost[i - 1] + labs(pla[i - 1].x, pla[i].x) + labs(pla[i - 1].y, pla[i].y);
    }

    LL ans = 0; LL nc, tmp;
    for(int i = 0; i < cnt; i++){
        nc = labs(xs, pla[i].x) + labs(ys, pla[i].y);
        for(int j = cnt - 1; j >= 0; j--){
            if(j >= i){ // 往下走
                tmp = nc - cost[i] + cost[j];
                if(tmp < 0) continue;
                if(tmp <= t){
                    if(j - i + 1 > ans) ans = j - i + 1;
                }
            }
            else { // 往上走
                tmp = nc - cost[j] + cost[i];
                if(tmp < 0) continue;
                if(tmp <= t){
                    if(i - j + 1 > ans) ans = i - j + 1;
                }
            }
        }
    }

    printf("%I64d\n", ans);

    return 0;
}



写在最后:

  D题思路上是没问题的,但因为比较少做这种大数据的类型,所以测试的时候看了cf的数据,算是半完成了*1800的题吧。寒假快乐。

posted on 2020-01-22 10:33  Ayasan  阅读(200)  评论(0编辑  收藏  举报