Good Bye 2019

A - Card Game

题意:两个玩家,每次各出一张卡,每张卡是不同的,然后出分数大的卡的玩家收走两张卡。求最优策略下谁把所有卡拿走。

题解:每次都出最大的那张。

void test_case() {
    int n, k1, k2;
    scanf("%d%d%d", &n, &k1, &k2);
    int max1 = 0, max2 = 0;
    for(int i = 1; i <= k1; ++i) {
        int a;
        scanf("%d", &a);
        max1 = max(max1, a);
    }
    for(int i = 1; i <= k2; ++i) {
        int a;
        scanf("%d", &a);
        max2 = max(max2, a);
    }
    if(max1 > max2)
        puts("YES");
    else
        puts("NO");
}

B - Interesting Subarray

题意:当一个数组中,出现一个子数组,其中的最大值-最小值>=子数组长度,那么这个数组就是有趣的。问一个数组是不是有趣的。

题解:一开始还搞什么双指针?保留窗口的两端是极值。但是这个算法是错的因为并不一定是极值才能导致数组有趣。正确的做法是判断是不是有一对相邻元素相差>=2。

证明:当有一对相邻元素相差>=2,那么取这一对元素就是答案,充分性得证。

否则,相邻元素的差至多为1,l长度的数组至多向同一个方向改变l-1,不满足题意,必要性得证。

int a[200005];
 
void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for(int i = 2; i <= n; ++i) {
        if(abs(a[i] - a[i - 1]) >= 2) {
            puts("YES");
            printf("%d %d\n", i - 1, i);
            return;
        }
    }
    puts("NO");
}

C - Make Good

题意:给一个序列,往这个序列后加至多3个元素,使得加法和是其异或和的两倍。

题解:异或和……数据结构就分解,构造就先搞个异或和上去。假如某个神秘力量启示我们搞个异或和上去,那么原来的序列就变成:

加法和:某个非负数x

异或和:0

问题变成加至多2个元素,使得加法和是异或和的两倍。

那么就加个x。

其中,上面的步骤可以分开做的原因是加法和异或都满足结合律。

ll a[200005];
 
void test_case() {
    int n;
    scanf("%d", &n);
    ll sum = 0, xorsum = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%lld", &a[i]);
        xorsum ^= a[i];
        sum += a[i];
    }
    ll b[10];
    b[1] = xorsum;
    sum += xorsum;
    b[2] = sum;
    printf("2\n%lld %lld\n", b[1], b[2]);
}

所以这个题还真是1500的,怪不得是手速场。

*D - Strange Device

题意:交互题。有一个装置,其中有 \(n\) 个不同元素。每次询问恰好 \(k(1\leq k< n)\) 个位置,然后装置会回答这些位置中,第 \(m\) 小的元素的位置和值,需要使用不超过 \(n\) 次询问,求出 \(m\) 的值(回答不算询问)。

题解:大小是无关紧要的,直接离散化。一开始考虑选一个区间平移来询问,事实上不能这么做,但是思路是相似的,用相邻的询问的特征来判断改变的元素与 \(m\) 的大小关系。选取其中的 \(k+1\) 个元素,第 \(i\) 次询问就除了第 \(i\) 个位置不询问,其他的这些位置都询问,这时若去掉了比 \(m\) 的元素小的元素或 \(m\) 本身,例如 \(n=6,a=(1,2,3,4,5,6),k=5,m=4,q_1=(2,3,4,5,6),a_1=5,q_2=(1,3,4,5,6),a_2=5,q_3=(1,2,4,5,6),a_3=5,q_4=(1,2,3,5,6),a_3=5\) 回答的都是的 \(m+1\) 。若去掉了比 \(m\) 大的元素,那么返回的就显然是 \(m\)

那么就统计一下他们出现的次数,注意到,若 \(m=1\) 则上面会回答1次2,5次1。若 \(m=2\) 则上面会回答2次3,4次2。依次类推,也就是,大的那个数出现的次数,就是 \(m\)

反思:其实假如不进行想象和形式化很重的推理,说不定能在当时观察到规律。所以还是要沉下心推几个样例。因为这个只告诉询问的第 \(m\) 大,所以确实从与 \(m\) 的大小关系入手是自然的。

int x, y;
int cntx, cnty;

void query(int id, int k) {
    putchar('?');
    for(int i = 1; i <= k + 1; ++i) {
        if(i == id)
            continue;
        putchar(' ');
        printf("%d", i);
    }
    putchar('\n');
    fflush(stdout);
    int p, v;
    scanf("%d%d", &p, &v);
    if(x == 0 || v == x) {
        x = v;
        ++cntx;
    } else {
        y = v;
        ++cnty;
    }
}

void test_case() {
    int n, k;
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= k + 1; ++i)
        query(i, k);
    if(x > y) {
        swap(x, y);
        swap(cntx, cnty);
    }
    printf("! %d\n", cnty);
}
posted @ 2020-01-04 16:24  KisekiPurin2019  阅读(155)  评论(0编辑  收藏  举报