Solution CF2096G Wonderful Guessing Game

首先考虑弱化。若没有询问被忽略则考虑每次询问直接分成三组,两组大小相等作为询问数组的前后部分,最后一组代表不在数组内。

由于将一个数均分为三组其中至少有两组大小相等因此可以直接分。询问次数 \(\lceil\log_3n\rceil\)

考虑将每个点看成位数为 \(q\) 的三进制数,则只要不存在两个数相等最后就是可以区分这些数字的。

考虑如果可以忽略一组答案,则对于 \(x,y\) 两个数,如果它们的三进制表示只相差一位则回答时可忽略这一位,这样这两个数就无法区分了。

因此我们可以转化题目:需要找出 \(n\)\(q\) 位三进制数,两两不同的位数量至少为 \(2\),问最小的 \(q\)

观察发现此时的 \(q\) 一定为 \(\lceil\log_3n\rceil+1\)。考虑多出来的这一位,我们需要让之前三进制下只相差一位的数字在这一位不同。可以想到按之前位数的和模 \(3\) 分组,此时之前相差一位的数一定被分到不同的组内。

同时发现分出的这三组一定有至少两组大小相等。最后一次操作只需要询问这两组拼起来即可。

const int N=200005;
int n,q,a[N][21];
char tmp[21];int ans[21];

il void sol(int l,int r,int d){
    // cerr<<l<<' '<<r<<'\n';
    if(r-l+1==1)return;
    else if(r-l+1==2)return a[l][d]=1,a[r][d]=2,void();
    int c=(r-l+1)/3;
    if(c*3+2==r-l+1){
        c++;
        forto(i,r-c+1,r)a[i][d]=1;
        forto(i,r-c*2+1,r-c)a[i][d]=2;
        sol(l,r-c*2,d+1),sol(r-c*2+1,r-c,d+1),sol(r-c+1,r,d+1);
    }else{
        forto(i,l,l+c-1)a[i][d]=1;
        forto(i,l+c,l+c*2-1)a[i][d]=2;
        sol(l,l+c-1,d+1),sol(l+c,l+c*2-1,d+1),sol(l+c*2,r,d+1);
    }
}

il void work(){
    scanf("%d",&n);
    int tot=1;q=0;
    while(tot<n)tot*=3,q++;
    forto(i,1,n)forto(j,1,q+1)a[i][j]=0;
    sol(1,n,1);
    vector<int>p1,p2;int c;
    forto(i,1,n){
        c=0;forto(j,1,q)c=(c+a[i][j])%3;
        a[i][q+1]=c;
    }
    forto(i,min(p1.size(),p2.size()),(int)p1.size()-1)a[p1[i]][q+1]=0;
    forto(i,min(p1.size(),p2.size()),(int)p2.size()-1)a[p2[i]][q+1]=0;
    q++,printf("%d\n",q);
    forto(i,1,q){
        p1.clear(),p2.clear();
        forto(j,1,n){
            if(a[j][i]==1)p1.eb(j);
            else if(a[j][i]==2)p2.eb(j);
        }
        printf("%d ",p1.size()+p2.size());
        for(int x:p1)printf("%d ",x);
        for(int x:p2)printf("%d ",x);
        printf("\n");
    }
    fflush(stdout),scanf("%s",tmp+1);
    forto(i,1,q){
        if(tmp[i]=='L')ans[i]=1;else if(tmp[i]=='R')ans[i]=2;
        else if(tmp[i]=='N')ans[i]=0;else ans[i]=-1;
    }
    forto(i,1,n){
        bool f=1;
        forto(j,1,q)if(ans[j]!=-1&&ans[j]!=a[i][j]){f=0;break;}
        if(f){printf("%d\n",i);break;}
    }
    fflush(stdout);
}

signed main(){
    int t;scanf("%d",&t);
    while(t--)work();
    return 0;
}
posted @ 2025-05-13 11:38  UniGravity_qwq  阅读(9)  评论(0)    收藏  举报