CF835E-The penguin‘s game【互動】
阿新 • • 發佈:2021-08-08
正題
題目連結:https://www.luogu.com.cn/problem/CF835E
題目大意
長度為\(n\)的序列中有兩個\(y\)其他都是\(x\),給出\(n,x,y\)。你每次可以詢問一個下標集合的數字異或和,要求在\(19\)次以內找到這兩個\(y\)的位置。
\(1\leq n\leq 1000,1\leq x,y\leq 10^9,x\neq y\)
解題思路
考慮詢問一個集合我們會得到的答案情況,如果集合大小為奇數則為\(y\)或者\(x\)依次表示\(y\)分別在一個集合內或者都在某個集合中,而偶數則是\(x\ xor\ y\)或者\(0\)。
現在變為了我們可以詢問一個集合回答兩個\(y\)
考慮到兩個數字的下標肯定有一個二進位制位不同,我們可以列舉這個位然後詢問這個位是\(1\)的元素。這樣我們總能找到一個集合使得一個\(y\)在內,一個\(y\)在外。
如果在這個兩個集合裡面暴力問的話算上前面的次數大概是\(3\log n\)的,考慮優化。
發現對於前面的詢問,我們可以得到兩個集合下標的異或值,所以如果我們問出一個位置再異或出另一個就好了。
因為是\(19\)次所以我們考慮找比較小的那個集合的值就好了。
code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1100; int n,x,y,cnt,p[N],ansa,ansb; void Ask(int l,int r){ if(l==r){ansa=p[l];return;} int mid=(l+r)>>1,ans,flag=0; printf("? %d",mid-l+1); for(int i=l;i<=mid;i++) printf(" %d",p[i]); putchar('\n');fflush(stdout); scanf("%d",&ans); if((mid-l+1)&1)flag=(ans==y); else flag=(ans==(x^y)); if(flag)Ask(l,mid); else Ask(mid+1,r); return; } void Find(int z){ cnt=0; for(int i=1;i<=n;i++) if((i/z)&1)p[++cnt]=i; if(cnt>(n/2)){ cnt=0; for(int i=1;i<=n;i++) if(!((i/z)&1))p[++cnt]=i; } Ask(1,cnt); } int main() { scanf("%d%d%d",&n,&x,&y); bool has=0; for(int z=1;z<=n;z<<=1){ int L=0,ans,flag=0;putchar('?'); for(int i=1;i<=n;i++)if((i/z)&1)L++; printf(" %d",L); for(int i=1;i<=n;i++)if((i/z)&1)printf(" %d",i); putchar('\n');fflush(stdout); scanf("%d",&ans); if(L&1)flag=(ans==y); else flag=(ans==(x^y)); if(flag&&!has)Find(z),has=1; ansb^=z*flag; } ansb^=ansa; if(ansa>ansb)swap(ansa,ansb); printf("! %d %d\n",ansa,ansb); fflush(stdout); return 0; }