P7339 『MdOI R4』Kotori 题解

发布于 2021-03-11  192 次阅读


题目链接

文章目录

解析

题意就不多阐述了,看原题就够了。个人觉得这是一道练归并排序性质的好题(虽然动规/堆维护等方法似乎也可行,但我认为最朴素的方法才是最美丽的)。

再发觉贪心不可行时,我们考虑分治维护每一轮比赛可能获胜的人。假设我们以及维护好了左区间(即上一轮比赛)哪些人可能能在上一轮获得胜利(也就是能活到当前这轮)以及右区间的这个信息,我们考虑如何合并。显然,我们发现,如果想要让左区间的某个人获胜,只需要这个人能够击败右区间尚存的最弱的那个人就可以了,右区间亦然。

这时候我们考虑如何维护区间最弱的人这个信息。在分治这个大背景下,我们容易想到归并排序——每个区间在合并时都是有序的,只需要取出第一个数就是最小的。同时我们还可以在归并排序时每次返回改区间尚存的人数,这样,我们就一次性维护好了尚存的人和区间最弱的人这两个信息。

依次合并,最后判断尚存的人中是否有 $1$ 号选手即可。代码写了一些注释。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
struct node{
    int val,id;//记录每个元素的支持人数和序号,序号用来判结果
}a[N],b[N];
int T,k,m;
inline int merge(int l,int r){//返回该轮尚存的人数
    if(l==r) return 1;
    int mid=(l+r)>>1;
    int L=merge(l,mid)+l-1;//记录左区间尚存的人的位置
    int R=merge(mid+1,r)+mid;//同上
    int i=l,j=mid+1,cnt=l-1;
    if(a[i].val<=a[j].val){//第一个数比大小,显然,第一个数大的那边的所有尚存选手都可以继续存活
        while(i<=L&&a[i].val+m<a[j].val) i++;//因为 kotori 可以定胜负,所以用 < 号。
    }
    else{
        while(j<=R&&a[j].val+m<a[i].val) j++;
    }
    while(i<=L&&j<=R){//正常归并排序,就是左右区间的上界变了而已
        if(a[i].val<a[j].val) b[++cnt]=a[i++];
        else b[++cnt]=a[j++];
    }
    while(i<=L) b[++cnt]=a[i++];
    while(j<=R) b[++cnt]=a[j++];
    for(int i=l;i<=cnt;i++){//容易发现,每次归并了之后,只有前 cnt 个人是有效的,且他们有序
        a[i]=b[i];
    }
    return cnt-l+1;//返回该轮尚存的人数
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&k,&m);
        for(int i=1;i<=(1<<k);i++){
            scanf("%d",&a[i].val);
            a[i].id=i;
        }
        int ans=merge(1,1<<k);
        bool fl=false;
        for(int i=1;i<=ans;i++){
            if(a[i].id==1){//找一找 1 号选手是否或者即可
                printf("Kotori\n");
                fl=true;break;
            }
        }
        if(!fl) printf("Yoshino\n");
    }
    return 0;
}

月流华 岁遗沙 万古吴钩出玉匣