Codeforces Round #604 (Div. 2)

https://codeforces.com/contest/1265

这场的2E是1C的不带checkpoints的版本。

A - Beautiful String

题意:给一个由'a','b','c','?'组成的字符串,把'?'填成前面三种其中之一使得字符串中没有连续两个相同的字符。

首先把本身就非法的、全部都是问号的,以及所有的单独的一个问号解决掉,那么剩下的字符串必定满足:至少有一个非问号字符,且没有两个连续的非问号字符,且没有单独的问号,这样是一定有解的,构造如下。

那么假如有:

"??aba???bc???bcb???"

那么等价于:

"a?aba??abc??cbcb??b"

意思是每段至少2个连续的问号里的第1个问号变成问号之后的第一个字符,若这段是最后一段问号则把最后一个问号变成其前一个。

剩下的一段都填两端没有的字符。

char s[MAXN + 5];
int nxt[MAXN + 5];

char ch[128][128], ch2[128][2];

void init() {
    memset(ch, '?', sizeof(ch));
    ch['a']['a'] = 'b';
    ch['a']['b'] = 'c';
    ch['a']['c'] = 'b';
    ch['b']['a'] = 'c';
    ch['b']['b'] = 'a';
    ch['b']['c'] = 'a';
    ch['c']['a'] = 'b';
    ch['c']['b'] = 'a';
    ch['c']['c'] = 'a';
    ch2['a'][0] = 'b';
    ch2['a'][1] = 'c';
    ch2['b'][0] = 'a';
    ch2['b'][1] = 'c';
    ch2['c'][0] = 'a';
    ch2['c'][1] = 'b';
}

void test_case() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for(int i = 2; i <= n; ++i) {
        if(s[i] != '?' && s[i - 1] != '?' && s[i] == s[i - 1]) {
            puts("-1");
            return;
        }
    }
    //上面去除了本身非法的情况,剩下的是一定能构造的
    bool suc = 1;
    for(int i = 1; i <= n; ++i) {
        if(s[i] != '?') {
            suc = 0;
            break;
        }
    }
    if(suc) {
        for(int i = 1; i <= n; ++i)
            putchar('a' + (i & 1));
        putchar('\n');
        return;
    }
    //上面去除了全部都是'?'的情况
    for(int i = 1; i <= n; ++i) {
        if(s[i] == '?') {
            if(i == 1)
                s[1] = ch[s[2]][s[2]];
            else if(i == n)
                s[n] = ch[s[n - 1]][s[n - 1]];
            else
                s[i] = ch[s[i - 1]][s[i + 1]];
        }
    }
    //上面去除了单独的'?'
    if(s[n] == '?') {
        int i = n - 1;
        while(s[i] == '?')
            --i;
        s[n] = s[i];
    }
    //上面保证了末尾一定不是'?'
    int pos = -1;
    for(int i = n; i >= 1; --i) {
        if(s[i] != '?') {
            pos = i;
            nxt[i] = -1;
        } else
            nxt[i] = pos;
    }
    for(int i = 1; i <= n; ++i) {
        if((i == 1 || s[i - 1] != '?') && s[i] == '?') {
            //每段的第一个问号,假如他前面
            if(i >= 1 && s[i - 1] == s[nxt[i]])
                continue;
            else
                s[i] = s[nxt[i]];
        }
    }
    //上面保证了每个问号的前后都是相同的
    for(int i = 1; i <= n; ++i) {
        if(s[i] == '?') {
            for(int j = i; j < nxt[i]; ++j)
                s[j] = ch2[s[i - 1]][j & 1];
            i = nxt[i] - 1;
        }
    }
    puts(s + 1);
}

但是实际上!连续三个字符怎么会有重复的呢?随便填就完事了。签到题都是怎么暴力怎么来的,一定是自己想复杂了。

char s[MAXN + 5];

void test_case() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for(int i = 2; i <= n; ++i) {
        if(s[i] != '?' && s[i - 1] != '?' && s[i] == s[i - 1]) {
            puts("-1");
            return;
        }
    }
    //上面去除了本身非法的情况,剩下的是一定能构造的
    bool suc = 1;
    for(int i = 1; i <= n; ++i) {
        if(s[i] == '?') {
            for(char c = 'a'; c <= 'c'; ++c) {
                bool suc = 1;
                s[i] = c;
                if(i > 1 && s[i] == s[i - 1])
                    suc = 0;
                if(i < n && s[i] == s[i + 1])
                    suc = 0;
                if(suc)
                    break;
            }
        }
    }
    puts(s + 1);
}

B - Beautiful Numbers

题意:给一个数组是一个[1,n]的permutation。对每个m∈[1,n]问[1,m]是否连续存在在这个数组中。

题解:

首先,[1,1]一定存在。

然后向指定方向扩展到2,若经过2以外的数,把2标记为不存在。

3已经被扩展,或3在区间左右?是:否。

所以每次就问新的数是不是在已有区间中或者左右,是则包含,否则扩展到有为止。

int pos[MAXN + 5];

void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1, ai; i <= n; ++i) {
        scanf("%d", &ai);
        pos[ai] = i;
    }
    int l = n + 1, r = 0;
    for(int i = 1; i <= n; ++i) {
        l = min(l, pos[i]);
        r = max(r, pos[i]);
        putchar('0' + (r - l + 1 == i));
    }
    putchar('\n');
}

事实上判断一下区间的长度就可以了。

C - Beautiful Regional Contest

题意:给一场Regional发奖牌,奖牌分为氪金牌g、卖银牌s和炼铜牌b三种。,要求满足以下条件:

1、g>0&&s>0&&b>0
2、g<s&&g<b
3、g+s+b<=n/2

同题数的同牌,每个等级都比下一等级至少多一题(区分度极高)。

在此基础上IUPC当然要扩大赛区规模,最好搞到400个队!这样就可以装满一列火车然后在火车站比赛了。主办方要求尽可能多发牌。

题解:

首先最麻烦的条件是“尽可能多发牌”,否则直接贪心:同第一名的全部金,然后发银直到比金多,然后发铜直到比金多。假如可以使得b>g&&g+s+b<=n/2则这个是发票数最小的一个解。

已知假如发牌数一定,把若奖牌跨至少两种题数,那么降级它会更好,因为g->s更容易使得g<s,而在s>=g的前提下可以尽可能补充b的数量。

所以直接找到最后一个可以发牌的人,这样发的奖牌是尽可能多的,也是最大可能满足g>0&&s>0&&b>0的,然后在此基础上发最少的g,最大可能满足g<s&&g<b,在s>=g后尽可能发b最大可能满足g<s&&g<b。显然这样是最容易符合题意的一种构造。

int a[MAXN + 5];

void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    int last = n / 2;
    if(a[last] == a[last + 1]) {
        while(last - 1 >= 1 && a[last - 1] == a[last])
            --last;
        --last;
    }
    int g = 0, s = 0, b = 0, i = 1, j;
    j = i;
    for(; i <= last; ++i) {
        if(a[i] == a[j])
            ++g;
        else
            break;
    }
    j = i;
    for(; i <= last; ++i) {
        if(a[i] == a[j])
            ++s;
        else if(s <= g) {
            j = i;
            ++s;
        } else
            break;
    }
    b = last - i + 1;
    if(b <= g) {
        printf("0 0 0\n");
        return;
    }
    printf("%d %d %d\n", g, s, b);
}

D - Beautiful Sequence

题意:给出0,1,2,3,这4种数字的数量,适当排列他们使得相邻元素的差恰好为1。无解-1。

题解:

首先观察一个等价性:

0 1 2 3 2 1' 2' 3

等价于

0 1 2 1' 2' 3 2 3

也等价于

0 1' 2' 1 2 3 2 3

事实上一组元素可以类似这样穿行,但是不知道怎么严格证明。所以合法的构造等价于[0,1][1,2][2,3]这样堆在一起。

补充:证明:(1,2)的左边可以接(1,0),也可以接(1,2),也可以接(3,2),事实上满足奇偶就可以,右边同理。那么把原本的元素编组出这些接口之后,(1,2)可以在其中任意活动,所以不妨全部丢在中间,左边可能会有多出来的接口(0),右边可能有多出来的接口(3),最后一定满足:(0)(1,0)(1,0)...(1,2)(1,2)...(3,2)(3,2)(3)。注意合法的条件就是中间至少有一个(1,2)

所以说一开始的想法就是对的,直接这样贪。

所以可以枚举是0开头还是1开头,两种之一就是答案,或者分析他们的数量关系严格构造:以上面的例子为例把所有的[0,1](必须以1结尾)和[2,3](必须以2开头)取出,剩下的一定是若干对(2,1),若1多1个可以把这个1插在最前面的0的前面,若2多1个可以把这个2插在最后面的3后面。其他情况无解。

当然当时我是先讨论掉没有0或者没有3的简单情况的。

注意上面这个算法的条件是可以形成若干个这样的接口,而接口的条件要1不比0少且2不比3少。

假如不做这个分类讨论,则要改为判定接口是否成功生成,若生成则必须接上。(其实是真的煞笔,一定是要一半奇数一半偶数的,然后奇数偶数隔着放,肯定是尽可能把差值近的丢在一起,然后特判一下是不是有0到3的情况就行了)

int s[MAXN + 5];

void test_case() {
    int a[4];
    scanf("%d%d%d%d", &a[0], &a[1], &a[2], &a[3]);
    if(a[0] > a[1] + 1 || a[3] > a[2] + 1) {
        puts("NO");
        return;
    }
    if(a[0] == a[1] + 1 && (a[2] + a[3] != 0)) {
        puts("NO");
        return;
    }
    if(a[3] == a[2] + 1 && (a[1] + a[0] != 0)) {
        puts("NO");
        return;
    }
    int n = a[0] + a[1] + a[2] + a[3];
    if(a[1] - a[0] == a[2] - a[3] || a[1] - a[0] + 1 == a[2] - a[3]) {
        int i;
        for(i = 1; a[0]--; i += 2)
            s[i] = 0;
        for(; a[2]--; i += 2)
            s[i] = 2;
        for(i = 2; a[1]--; i += 2)
            s[i] = 1;
        for(; a[3]--; i += 2)
            s[i] = 3;
        puts("YES");
        for(int i = 1; i <= n; ++i)
            printf("%d%c", s[i], " \n"[i == n]);
        return;
    }
    if(a[1] - a[0] == a[2] - a[3] + 1) {
        int i;
        for(i = 1; a[1]--; i += 2)
            s[i] = 1;
        for(; a[3]--; i += 2)
            s[i] = 3;
        for(i = 2; a[0]--; i += 2)
            s[i] = 0;
        for(; a[2]--; i += 2)
            s[i] = 2;
        puts("YES");
        for(int i = 1; i <= n; ++i)
            printf("%d%c", s[i], " \n"[i == n]);
        return;
    }
    puts("NO");
    return;
}

2E - Beautiful Mirrors

这个是1C的不带checkpoints的版本。

题意:有n个格子,你从1号格子开始走,在格子i上面有pi的概率往i+1走,若走到n+1就结束,否则回到1号重来。求结束的期望步数。

题解:设dp[i]表示从第i个格子开始走,到结束的期望步数,则答案为dp[1]。

若设 \(dp_{n+1}=0\) ,则对所有的i都有:

\(dp_{i}=p_{i}(1+dp_{i+1})+(1-p_{i})(1+dp_{1})\)

即:
\(dp_{i}=1+p_{i}dp_{i+1}+(1-p_{i})dp_{1}\)

特别的:
\(dp_{1}=1+p_{1}dp_{2}+(1-p_{1})dp_{1}\)

即:
\(dp_{1}=\frac{1}{p_{1}}+dp_{2}\)

然后:
\(dp_{2}=1+p_{2}dp_{3}+(1-p_{2})dp_{1}\)

即:
\(dp_{2}=1+p_{2}dp_{3}+(1-p_{2})(\frac{1}{p_{1}}+dp_{2})\)

\(dp_{2}=\frac{1+p_{1}-p_{2}}{p_{1}p_{2}}+dp_{3}\)

\(dp_{2}=\frac{q_2+p_{1}}{p_{1}p_{2}}+dp_{3}\)

再算出:

\(dp_{3}=\frac{q_3(2p_1+p_2)+p_1p_2}{p_{1}p_{2}p_3}+dp_{4}\)

太难算了。

问了一下群友,群友给了一个看不懂的方程。

假如设从1开始第一次走到第i个格子的期望天数是dp[i],那么

\(dp[1]=0\)

从第i个格子走到第i+1个格子,有p[i]的概率成功此时支付dp[i]+1,有q[i]的概率失败,此时已经支付了dp[i]+1,然后在1,从1到i+1的价格恰好就是dp[i+1]进入套娃。

假如粗暴一点,失败的就付出已支付的成本,然后转回来?

\(dp[i+1]=p[i](dp[i]+1)+q[i](dp[i]+1+dp[i+1])\)

\(p[i]dp[i+1]=p[i](dp[i]+1)+q[i](dp[i]+1)\)

\(dp[i+1]=\frac{1}{p[i]}(dp[i]+1)\)

过了第二个样例了?那就这样写。AC全靠猜?大力套样例?

ll p[MAXN + 5];
ll dp[MAXN + 5];

const ll inv100 = qpow(100, MOD - 2);

void test_case() {
    int n;
    scanf("%d", &n);
    dp[1] = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%lld", &p[i]);
        p[i] = p[i] * inv100 % MOD;
        dp[i + 1] = qpow(p[i], MOD - 2) * (dp[i] + 1) % MOD;
    }
    printf("%lld\n", dp[n + 1]);
}

1C - Beautiful Mirrors with queries

posted @ 2019-12-06 10:50  KisekiPurin2019  阅读(254)  评论(0编辑  收藏  举报