Codeforces Round #616 (Div. 2)

Posted kisekipurin2019

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #616 (Div. 2)相关的知识,希望对你有一定的参考价值。

题目链接:https://codeforces.com/contest/1291

好演哦,可能我累了吧,只会AB,C吃完KFC之后就懂了233。可能是这次的2019-nCoV让我也很不正常吧。

A - Even But Not Even

题意:给一个十进制数字字符串,从中取一个非空子序列,使得这个子序列代表的数字不是偶数,但各个数字位加起来的和是偶数。

题解:不是偶数要求结尾一定是奇数,再要求各个数字位的和是偶数所以至少需要两个奇数,而两个奇数确实也满足题意。

char s[200005];
char t[200005];
void test_case() {
    int n;
    scanf("%d%s", &n, s + 1);
    int top = 0;
    for(int i = 1; i <= n; ++i) {
        if((s[i] - '0') % 2 == 1) {
            t[++top] = s[i];
            if(top >= 2)
                break;
        }
    }
    if(top <= 1)
        puts("-1");
    else
        printf("%c%c
", t[1], t[2]);
}

B - Array Sharpening

想了很多种奇奇怪怪的贪心,都会被

2
7
1 2 4 3 2 1 0
8
0 1 2 4 3 2 1 0

这组数据hack。

事实上有个很显然的 (O(n^2)) 的解法,枚举每个peek的位置,那么构造的方案一定是两边是 (0) ,然后向中间走直到碰到peek位置为止逐个递增1。那么可以维护一个布尔值的前缀和和后缀和,分别表示它前面的数能不能表示 (0 1 2 ... i) 和它后面的数字能不能表示 (i i-1 i-2 ... 0) 。中间这个peek必须大于其左边和其右边的最大值。

int n;
int x[300005];
bool a[300005];
bool pa[300005];
bool d[300005];
bool pd[300005];
 
void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i){
        scanf("%d", &x[i]);
        a[i]=0;
        pa[i]=0;
        d[i]=0;
        pd[i]=0;
    }
    if(n == 1) {
        puts("Yes");
        return;
    }
    if(n == 2) {
        if(x[1] == 0 && x[2] == 0) {
            puts("No");
            return;
        }
        puts("Yes");
        return;
    }
    for(int i = 1; i <= n; ++i) {
        if(x[i] >= i - 1)
            a[i] = 1;
    }
    pa[1] = a[1];
    for(int i = 2; i <= n; ++i)
        pa[i] = pa[i - 1] & a[i];
    for(int i = n; i >= 1; --i) {
        if(x[i] >= n - i)
            d[i] = 1;
    }
    pd[n] = d[n];
    for(int i = n - 1; i >= 1; --i)
        pd[i] = pd[i + 1] & d[i];
    if(pa[n] || pd[1]) {
        puts("Yes");
        return;
    }
    /*for(int i=1;i<=n;++i)
        printf("%d%c",(int)a[i]," 
"[i==n]);
    for(int i=1;i<=n;++i)
        printf("%d%c",(int)pa[i]," 
"[i==n]);
    for(int i=1;i<=n;++i)
        printf("%d%c",(int)d[i]," 
"[i==n]);
    for(int i=1;i<=n;++i)
        printf("%d%c",(int)pd[i]," 
"[i==n]);*/
    for(int i = 2; i <= n - 1; ++i) {
        if(x[i] >= max(i - 1, n - i) && pa[i - 1] && pd[i + 1]) {
            puts("Yes");
            return;
        }
    }
    puts("No");
    return;
}

C - Mind Control

题意:有 (n(1 leq n leq 3500)) 个人排成一个队列A,恰好有 (n) 个数字排成一个双端队列B,每次A队列队首的人就会选择双端队列B的队首队尾两端其中之一进行Pop。你在队列A的第 (m(1leq m leq n)) 个位置,你可以说服至多 (k(1leq k leq n)) 个人,让他固定拿双端队列B的队首或者队尾,求你能够拿到的数字的最大值。

题解:一个 (O(n)) 的做法。首先可以取 (k:=min(k,m-1)) ,因为排在你后面的人是不会影响的。然后考虑一个规律:

先看说服 (0) 个人

基本情况1:假如前 (m-1) 个都拿双端队列B的队尾,自己可以选择的就是 (1)(n-m+1) 其中之一,那么肯定选 (max(a[1],a[n-m+1]))

基本情况2:假如前 (m-2) 个都拿双端队列B的队尾, (1) 个人拿双端队列B的队首,自己可以选择的就是 (2)(n-m+2) 其中之一,那么肯定选 (max(a[2],a[n-m+2]))

……

基本情况m-1:假如前 (m-1) 个人都拿双端队列B的队首,自己可以选择的就是 (m)(n) 其中之一,那么肯定选 (max(a[m],a[n]))

上面这些情况都有可能发生,所以取这堆数的最小值。

再看说服 (1) 个人

情况1‘:说服他必须拿队首,那么去除掉上面的基本情况1。

情况2‘:说服他必须拿队尾,则去除掉上面的基本情况m-1。

再看说服 (2) 个人

情况1‘:说服两人都必须拿队首,那么去除掉上面的基本情况1和基本情况2。

情况2‘:说服两人分别拿队首队尾,那么去除掉上面的基本情况1和基本情况m-1。

情况3‘:说服两人都必须拿队尾,那么去除掉上面的基本情况m-2和基本情况m-1。


枚举每种说服 (k_i) 个人的情形可以用尺取的方法维护,比如说服 (2) 个人,就可以先求出情况1‘的最小值;然后加入基本情况2,去掉基本情况m-1,就求出情况2‘的最小值;然后加入基本情况1,去掉基本情况m-2,就求出情况3‘的最小值。那么枚举 (k_i) 就是一个 (O(n^2)) 的做法。

不过在这里就可以观察出,说服更多的人,只是让单个情况包含的基本情况变少,同时让尺取的步骤变多,说服更多的人可以让单次取最小值的区间最短,那么取最小值的区间越短肯定得到答案越大的机会越大。也就是说,通过最小值的贡献规律,就可以知道直接求出说服 (k) 个人的情况就可以了。

统计尺取的队列中的最小值,使用双栈队列可以做到均摊 (O(n))

struct Queue {
    static const int MAXN = 3500;
    static const int INF = 1061109567;
    int s1[MAXN + 5], s2[MAXN + 5];
    int s1top, s2top, s1min;

    void Clear() {
        s1top = 0;
        s2top = 0;
        s2[0] = INF;
        s1min = INF;
    }

    void Push(int x) {
        s1[++s1top] = x;
        s1min = min(s1min, x);
    }

    void Pop() {
        if(s2top)
            --s2top;
        else {
            while(s1top)
                s2[++s2top] = min(s2[s2top - 1], s1[s1top--]);
            --s2top;
            s1min = INF;
        }
    }

    int Size() {
        return s1top + s2top;
    }

    int Min() {
        return min(s2[s2top], s1min);
    }
} q;

int n, m, k;
int a[3505];

void test_case() {
    scanf("%d%d%d", &n, &m, &k);
    k = min(k, m - 1);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    q.Clear();
    for(int i = 1; i <= m - k; ++i)
        q.Push(max(a[i], a[n - m + i]));
    int ans = q.Min();
    for(int i = m - k + 1; n - m + i <= n; ++i) {
        q.Pop();
        q.Push(max(a[i], a[n - m + i]));
        ans = max(ans, q.Min());
    }
    printf("%d
", ans);
    return;
}

以上是关于Codeforces Round #616 (Div. 2)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #616 题解

Codeforces Round #616 (Div. 2)

Codeforces Round #616 (Div. 2)

Codeforces Round #616 (Div. 1)

Codeforces Round #616 部分题解

Codeforces Round #616(Div.2) Even But Not Even