1. 程式人生 > >Codeforces 835E E. The penguin's game 互動題 多豬試毒

Codeforces 835E E. The penguin's game 互動題 多豬試毒

給定n(2<=n<=1000)個數,有n-2個數是x,2個數是y,x與y非0且x與y不相等。每次向測評機詢問一些位置,測評機返回這些位置的數的異或值。最多可以詢問19次,要求輸出兩個y的位置。

解法:題目給出的19次詢問次數上限是嚴格的。即能構造資料使得不得不詢問19次才能出答案。

先討論出詢問的集合元素個數的奇偶性和y個數的奇偶性返回的答案分別是什麼。一共有四種情況,用(集合個數,y的個數,答案)表示的話,即有:

(偶數,偶數,0),(偶數,奇數,x xor y),(奇數,偶數,x),(奇數,奇數,y),因為x與y非零且互不相等,故0,x,y,x xor y互不相等。觀察到,如果返回x xor y或者y的時候,代表詢問的集合裡一定有且僅有一個y。

假設y的兩個位置為posa和posb,因為posa!=posb,故這兩個數的二進位制數中至少有一位是不同的。因此,我們可以花費至多10次詢問,第i次詢問第i位為1的位置組成的集合,若其異或值為x xor y或者y,則代表這個集合裡有且僅有一個y;否則,代表這個集合裡有0個或者2個y。任取一次返回異或值是x xor y或y的詢問所代表的那一位。這樣我們就能通過該位把整個集合分成兩個集合,每個集合都有且僅有一個y,且集合個數變成了2^9數量級,問題變成了原始的多豬試毒問題,只需要花費9次詢問就可以得到這個集合裡y的位置。這樣最壞一共花費了19次詢問,已經用完了題目所給的詢問次數。

那麼,另一個集合怎麼辦呢?其實,不需要再花9次詢問就可以解出另一個集合y的位置,甚至不需要再詢問了。因為,第一輪詢問中我們知道了posa和posb所不同的位數,顯然,剩下的那些位數就是相同的位數。我們用一個數bit來記錄這個資訊,若posa與posb的第i位相同,則bit的第i位為0;否則,bit的第i位為1。這樣,posb=bit xor posa,即可以直接通過第一輪詢問得到的bit以及第二輪詢問得到的posa求出posb。

下面是程式碼:

#include <bits/stdc++.h>
using namespace std;
int n,x,y,ret,bit,differ_bit,posa;
inline int query(vector<int> &ask) {
    if (!ask.size())
        return 0;
    putchar('?');
    printf(" %d ",ask.size());
    for (int i=0;i<(int)ask.size();++i)
        printf("%d%c",ask[i],i+1==(int)ask.size()?'\n':' ');
    fflush(stdout);
    int ret;
    scanf("%d",&ret);
    return ret;
}
int main()
{
    scanf("%d%d%d",&n,&x,&y);
    for (int i=0;i<10;++i) {
        vector<int> ask;
        for (int j=1;j<=n;++j)
            if ((j>>i)&1)
                ask.push_back(j);
        ret=query(ask);
        if (ret==y||ret==(x^y)) {
            bit^=1<<i;
            differ_bit=i;
        }
    }
    vector<int> left,right;
    for (int i=1;i<=n;++i)
        if (i&(1<<differ_bit))
            left.push_back(i);
        else
            right.push_back(i);
    for (int i=0;i<9;++i) {
        vector<int> ask;
        for (int j=0;j<(int)left.size();++j)
            if (j&(1<<i))
                ask.push_back(left[j]);
        ret=query(ask);
        if (ret==y||ret==(x^y))
            posa^=1<<i;
    }
    posa=left[posa];
    printf("! %d %d\n",min(posa,bit^posa),max(posa,bit^posa));
    return 0;
}