2024.10.14 Codeforces Round 978 (Div. 2)

EssentialSingularity發表於2024-10-29

比賽連結

Solved:4/7

Upsolved:5/7

Rank:447(rated 343)


D2. Asesino(Hard Version)

題意:

有 n 個人,除了一個臥底以外,其他人或者只會說真話,或者只會說謊,且他們知道彼此的身份。臥底只會說謊,但其他人都認為他只會說真話。現在你可以進行若干次詢問,每次詢問形如問第 i 個人第 j 個人是什麼身份。現在要用最少的詢問次數找到這個臥底。這裡的最少指:你的詢問次數不能超過“能保證在給定n的任何情況下都可以找到臥底”的詢問次數。

做法:

首先注意到如果a和b都不是臥底,那麼問(a,b)和問(b,a)得到的回答應該相同。

所以當n為偶數時,每次問(2k-1,2k),如果回答不同,那麼臥底就在兩人之間。這時再隨便找一個人(此時其他人都必不是臥底),問(2k-1,x)和(x,2k-1)。若回答仍然不同,則2k-1是臥底;否則2k是臥底。

注意如果問到n-2仍然沒問到臥底,那麼臥底一定在n-1和n之間,此時不用再浪費兩次機會,直接確定n-1是不是臥底即可。總次數為n。

當n為奇數時,如果和偶數一樣做,如果問到n-1都沒問到,最後會剩一個人,答案就是n-1;如果問到n-3之前就問到了,答案小於n。但唯一問題在於:如果臥底在n-2和n-1之間,那麼還需要2次機會來確定臥底,總共將需要n+1次。有什麼方法去掉多出來的1次嗎?

n=3時,不管怎麼試都至少需要4次。

n≥5為奇數時,可以注意到另一個性質:確定三個人都不是臥底只需要三次詢問——問(a,b),(b,c),(c,a),如果這三次詢問的異或和為1,那麼他們一定都不是臥底。

因此先問 12,23,31 確定臥底是否在 123 中。如果在,那麼再問 13 和 21 進一步確定臥底是誰。如果不在,和偶數一樣做。剩兩個人時直接問 (1,n) 和 (n,1)。這樣還是隻需要n次。

int n;
int qry(int x,int y){
    cout<<"? "<<x<<' '<<y<<endl;
    int res;
    cin>>res;
    return res;
}
void solve(){
    cin>>n;
    if(n==3){
        int x=qry(1,2),y=qry(2,1);
        if(x==y){cout<<"! "<<3<<endl;return;}
        else{
            int u=qry(1,3),v=qry(3,1);
            if(u==v){cout<<"! "<<2<<endl;return;}
            else{cout<<"! "<<1<<endl;return;}
        }
    }
    if(n&1){
        int u=qry(1,2),v=qry(2,3),w=qry(3,1);
        if(!(u^v^w)){
            int z=qry(1,3);
            if(z!=w){
                int t=qry(2,1);
                if(u==t){cout<<"! "<<3<<endl;return;}
                else{cout<<"! "<<1<<endl;return;}
            }
            else{cout<<"! "<<2<<endl;return;}
        }
        else{
            if(n==5){
                int u=qry(1,4),v=qry(4,1);
                if(u==v){cout<<"! "<<5<<endl;return;}
                else{cout<<"! "<<4<<endl;return;}
            }
            else{
                for(int i=4;i+3<=n;i+=2){
                    int x=qry(i,i+1),y=qry(i+1,i);
                    if(x!=y){
                        int u=qry(i,1),v=qry(1,i);
                        if(u!=v){cout<<"! "<<i<<endl;return;}
                        else{cout<<"! "<<i+1<<endl;return;}
                    }
                }
                int u=qry(1,n),v=qry(n,1);
                if(u==v){cout<<"! "<<n-1<<endl;return;}
                else{cout<<"! "<<n<<endl;return;}
            }
        }
    }
    for(int i=1;i+3<=n;i+=2){
        int x=qry(i,i+1),y=qry(i+1,i);
        if(x!=y){
            int u=qry(i,n),v=qry(n,i);
            if(u!=v){cout<<"! "<<i<<endl;return;}
            else{cout<<"! "<<i+1<<endl;return;}
        }
    }
    int u=qry(1,n),v=qry(n,1);
    if(u!=v){cout<<"! "<<n<<endl;return;}
    else{cout<<"! "<<n-1<<endl;return;}
}

怎麼證明小於n一定不能保證呢?

相關文章