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; }