zoukankan      html  css  js  c++  java
  • Codeforces Round #610 (Div. 2)

    A - Temporarily unavailable

    题意:给一个x轴,从x=a走到x=b,求其中断网的时间的,断网当且仅当你离路由器x=c距离超过r。

    题解:找c2<c1的bug找了半天。

    void test_case() {
        ll a, b, c, r;
        scanf("%lld%lld%lld%lld", &a, &b, &c, &r);
        if(a > b)
            swap(a, b);
        ll t = (b - a);
        ll c1 = c - r, c2 = c + r;
        c1 = max(a, c1);
        c2 = min(b, c2);
        ll tt = max(0ll, c2 - c1);
        printf("%lld
    ", t - tt);
    }
    

    B1 - K for the Price of One (Easy Version)

    见下

    B2 - K for the Price of One (Hard Version)

    题意:去商店买东西,商店有n个物品,每个物品有自己的价格,商店有个优惠活动,当你买恰好k个东西时可以只为其中最贵的那个付款,求有限的钱中买到的最多的物品数量,你可以多次使用优惠。

    题解:不管什么东西先排序,当时想到一个贪心:买了一个物品之后假如这个物品的序号>=k,那么它之前的k-1个物品一定就是被它免费的。剩下的用最前面的零头来凑。但是忽略了一个事实,就是买的东西实际上都是从序号1开始的连续的一段,中间是不会断开的。因为可以将用优惠买的物品的点水平左移,知道恰好把零头的后一个给免费了,这样数量不变,花的钱更少。

    int n, p, k;
    int a[200005];
    ll sum[200005];
    ll dp[200005];
     
    void test_case() {
        scanf("%d%d%d", &n, &p, &k);
        for(int i = 1; i <= n; ++i) 
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i <= n; ++i)
            sum[i] = sum[i - 1] + a[i];
        for(int i = 1; i < k; ++i)
            dp[i] = 0;
        for(int i = k; i <= n; ++i)
            dp[i] = dp[i - k] + a[i];
        int ans = 0;
        for(int i = 1; i < k; ++i) {
            if(sum[i] <= p)
                ans = i;
        }
        for(int i = k; i <= n; ++i) {
            int rp = p - dp[i];
            if(rp < 0)
                break;
            int cnt = 0;
            if(i % k)
                cnt = upper_bound(sum + 1, sum + 1 + (i % k), rp) - sum - 1;
            ans = max(ans, cnt + i / k * k);
        }
        printf("%d
    ", ans);
    }
    
    int n, p, k;
    int a[200005];
    ll dp[200005];
    
    void test_case() {
        scanf("%d%d%d", &n, &p, &k);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i < k; ++i)
            dp[i] = dp[i - 1] + a[i];
        for(int i = k; i <= n; ++i)
            dp[i] = dp[i - k] + a[i];
        int ans = 0;
        for(int i = 1; i <= n; ++i) {
            if(dp[i] <= p)
                ans = i;
        }
        printf("%d
    ", ans);
    }
    

    观察了一下题目貌似还可以这么写:

    int n, p, k;
    int a[200005];
    
    void test_case() {
        scanf("%d%d%d", &n, &p, &k);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + n);
        for(int i = 1; i < k; ++i)
            a[i] += a[i - 1];
        for(int i = k; i <= n; ++i)
            a[i] += a[i - k];
        while(a[n] > p)
            --n;
        printf("%d
    ", n);
    }
    

    C - Petya and Exam

    写完这道题居然能加分,而且出得这么快估计是1800的难度想不到官方也这么觉得。

    题意:参加一门课的考试,考试的规则非常的奇怪,和正常中国人见过的都不同。

    首先考试只有两种题,一种是简单题,每道题耗时固定为a;另一种是困难题,每道题耗时固定为b,保证b>a。分值都是1。

    考试的规则并不只是写多少题得多少分,鼓励提前交卷。假如你没有提前交卷,那么有一部分的题目会列为“必需”,当“必需”的题目没有全部被完成的话,这门课就算0分;否则得到与题数相同的分数,包括“必需”和“非必需”的。

    题解:很显然又直接按时间排升序,然后贪心,要在某个题变成必需的前夕进行判断。首先先得把“必需”的时间全部花出去(小心溢出和运算符优先级),剩下的尽可能填“非必需”的简单题,然后尽可能填“非必需”的困难题。这样写会漏一种情况,因为在最后一个变成“必需”之后没有下一个变成“必需”的前夕了,而是考试结束。所以先特判掉全部变成“必需”的情况是不是可以待到考试结束全部做完,然后再贪心。

    现在觉得好像把时间离散化的话就不需要使用nxt这种丑陋的写法了。

    int n, T, a, b;
    struct Problem {
        int t, d;
        bool operator<(const Problem& p)const {
            return t < p.t;
        }
    } p[200005];
    
    void test_case() {
        scanf("%d%d%d%d", &n, &T, &a, &b);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &p[i].d);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &p[i].t);
        int suma = 0, sumb = 0;
        for(int i = 1; i <= n; ++i) {
            if(p[i].d == 0)
                ++suma;
            else
                ++sumb;
        }
        if(1ll * suma * a + 1ll * sumb * b <= T) {
            printf("%d
    ", n);
            return;
        }
        sort(p + 1, p + 1 + n);
        int ans = 0, cura = 0, curb = 0;
        for(int i = 1; i <= n;) {
            int curt = p[i].t - 1;
            if(curt >= (1ll * cura * a + 1ll * curb * b)) {
                int rest = curt - (1ll * cura * a + 1ll * curb * b);
                int cnta = min(suma, rest / a);
                //这里并不可能溢出
                rest -= cnta * a;
                int cntb = min(sumb, rest / b);
                //这里并不可能溢出
                rest -= cntb * b;
                ans = max(ans, cura + curb + cnta + cntb);
            }
            int nxt = i;
            while(nxt <= n && p[nxt].t == p[i].t) {
                if(p[nxt].d == 0) {
                    ++cura;
                    --suma;
                } else {
                    ++curb;
                    --sumb;
                }
                ++nxt;
            }
            i = nxt;
        }
        printf("%d
    ", ans);
    }
    

    发现上面的写法里面有两个惊人的乘法没有变 long long 的,有点害怕。以后假如不卡时间可以直接上 long long 就不会出任何事。

    离散化的写法好像也没高明到哪里去:

    int n, T, a, b;
    struct Problem {
        int t, d;
        bool operator<(const Problem& p)const {
            return t < p.t;
        }
    } p[200005];
    int t[200005];
    int cura[200005], curb[200005];
    
    void test_case() {
        scanf("%d%d%d%d", &n, &T, &a, &b);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &p[i].d);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &p[i].t);
    
        int suma = 0, sumb = 0;
        for(int i = 1; i <= n; ++i) {
            if(p[i].d == 0)
                ++suma;
            else
                ++sumb;
        }
        if(1ll * suma * a + 1ll * sumb * b <= T) {
            printf("%d
    ", n);
            return;
        }
        for(int i = 1; i <= n; ++i)
            t[i] = p[i].t;
        sort(t + 1, t + 1 + n);
        int tn = unique(t + 1, t + 1 + n) - (t + 1);
        for(int i = 1; i <= tn; ++i)
            cura[i] = 0, curb[i] = 0;
        for(int i = 1; i <= n; ++i) {
            p[i].t = lower_bound(t + 1, t + 1 + tn, p[i].t) - t;
            if(p[i].d == 0)
                ++cura[p[i].t];
            else
                ++curb[p[i].t];
        }
        for(int i = 1; i <= tn; ++i) {
            cura[i] += cura[i - 1];
            curb[i] += curb[i - 1];
        }
        int ans = 0;
        for(int i = 1; i <= tn; ++i) {
            int curt = t[i] - 1;
            if(curt >= (1ll * cura[i - 1] * a + 1ll * curb[i - 1] * b)) {
                int rest = curt - (1ll * cura[i - 1] * a + 1ll * curb[i - 1] * b);
                int cnta = min(suma - cura[i - 1], rest / a);
                //这里并不可能溢出
                rest -= cnta * a;
                int cntb = min(sumb - curb[i - 1], rest / b);
                //这里并不可能溢出
                rest -= cntb * b;
                ans = max(ans, cura[i - 1] + curb[i - 1] + cnta + cntb);
            }
        }
        printf("%d
    ", ans);
    }
    

    D - Enchanted Artifact

    交互题,卡的还蛮紧,很多人都想出了n+3的解法。

    题意:猜一段只有'a'和'b'的字符串,连长度n未知,只能猜至多n+2次,每次jury返回一个值告诉你你猜的字符串和答案的“修改距离”(最短的使用替换、添加、删除你猜的串t,使得t好答案串s两个字符串相等),猜中0则成功。

    题解:首先处理掉数据量较小的情况?其实统一的解法里面不需要这么复杂。

    1、先只给一个字母'a',回答res。
    当res=0时,退出。
    当字符串为全a"aaa...a"时,长度为res+1。
    当字符串为全b"bbb...b"时,长度为res。
    当字符串为ab混合时,长度也是res+1。
    2、设len=res+1,给出长len的字符串全a"aaa...a",回答res。
    当字符串为全a"aaa...a"时,res=0,退出。
    当字符串为全b"bbb...b"时,res=len,再输出长度为len-1的全b串,走流程退出。
    当字符串为ab混合时,答案长度为len,res=b的数量。
    3、记pres=res,则pres就是b的数量。问长len的字符串"baaa...a",回答res。
    当res<pres,则首字母确实是b。
    否则,首字母是a。
    确定首字母之后循环执行这一步。

    int getAnswer() {
        fflush(stdout);
        int res;
        scanf("%d", &res);
        if(res == 0)
            exit(0);
        return res;
    }
    
    char s[305];
    
    void test_case() {
        int len, res, pres;
        puts("a");
        res = getAnswer();
        len = res + 1;
        for(int i = 1; i <= len; ++i)
            s[i] = 'a';
        puts(s + 1);
        res = getAnswer();
        if(res == len) {
            for(int i = 1; i <= len - 1; ++i)
                s[i] = 'b';
            s[len] = '';
            puts(s + 1);
            res = getAnswer();
            exit(-1);
        }
        pres = res;
        for(int i = 1; i <= len; ++i) {
            s[i] = 'b';
            puts(s + 1);
            res = getAnswer();
            if(res >= pres)
                s[i] = 'a';
        }
        exit(-1);
    }
    

    这个WA5了,想想。少了一句赋值 pres = res; ,加上之后WA67了???真就FST警告。原来是提问串>300的长度了,我晕了,还有这种细节的吗?

    int getAnswer() {
        fflush(stdout);
        int res;
        scanf("%d", &res);
        if(res == 0)
            exit(0);
        return res;
    }
    
    char s[305];
    
    void test_case() {
        int len, res, pres;
        puts("a");
        res = getAnswer();
        if(res == 300) {
            for(int i = 1; i <= res; ++i)
                s[i] = 'b';
            puts(s + 1);
            res = getAnswer();
            exit(-1);
        }
        len = res + 1;
        for(int i = 1; i <= len; ++i)
            s[i] = 'a';
        puts(s + 1);
        res = getAnswer();
        if(res == len) {
            for(int i = 1; i <= len - 1; ++i)
                s[i] = 'b';
            s[len] = '';
            puts(s + 1);
            res = getAnswer();
            exit(-1);
        }
        pres = res;
        for(int i = 1; i <= len; ++i) {
            s[i] = 'b';
            puts(s + 1);
            res = getAnswer();
            if(res >= pres)
                s[i] = 'a';
            pres = res;
        }
        exit(-1);
    }
    

    还好区域赛不搞这种交互题。

    看了一下学长的,直接问300个a,那么a的数量就是cnta=300-res,然后问cnta个a,那么b的数量就是cntb=res。这时假如是全a或者全b要特判一下。

    char s[305];
    
    int getAnswer(int len) {
        for(int i = 1; i <= len; ++i)
            putchar(s[i]);
        putchar('
    ');
        fflush(stdout);
        int res;
        scanf("%d", &res);
        if(res == 0)
            exit(0);
        return res;
    }
    
    void test_case() {
        int len = 0, res, pres;
        for(int i = 1; i <= 300; ++i)
            s[i] = 'a';
        res = getAnswer(300);
        len = 300 - res;
        if(len == 0) {
            s[1] = 'b';
            res = getAnswer(1);
            for(int i = 1; i <= res + 1; ++i)
                s[i] = 'b';
            res = getAnswer(res + 1);
            exit(-1);
        }
        res = getAnswer(len);
        len += res;
        pres = res;
        for(int i = 1; i <= len; ++i) {
            s[i] = 'b';
            res = getAnswer(len);
            if(res >= pres)
                s[i] = 'a';
            pres = res;
        }
        exit(-1);
    }
    
  • 相关阅读:
    使用自己的key对app进行签名
    pl/sql中文乱码解决办法
    Oracle存储过程中创建表的权限
    pl/sql中获得sql语句执行后影响的行数
    申请Android Map APIKey
    vs快捷键
    ODAC安装配置与使用详解
    .net不安装Oracle11g客户端直接使用ODAC
    android通过USB使用真机调试程序
    pl/sql中实现字符串分割
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12096103.html
Copyright © 2011-2022 走看看