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\n", 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\n", 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\n", 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", 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", 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\n", 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", 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\n", 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] = '\0';
        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] = '\0';
        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('\n');
    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);
}
posted @ 2019-12-25 13:12  KisekiPurin2019  阅读(243)  评论(0编辑  收藏  举报