なるほど…。
https://yukicoder.me/problems/no/3347
問題
各要素1~Nの値を持つ、長さNの数列Aを当てるinteractive問題である。
クエリとして整数列Bを指定すると、BがAの部分列かどうか判定してくれる。
このクエリを5000回まで行い、Aを特定せよ。
解法
Aを値の小さい順に特定させていく。
A(n) := Aのうちn以下の要素だけ抜き出した数列
とし、A(n)からA(n+1)を求めよう。
まず(n+1)の数を特定する。
これは(n+1)だけ繰り返した数列を、徐々に長くしていきながらクエリを実行すれば数を特定できる。
次にその(n+1)をA(n)のどこに挿入するか考える。
以下の値を考える。
f(n,m,k) := A(n)のうち、先頭m要素の後に(n+1)をk個繰り返したら、それはAの部分列かどうか
各kに対し、f(n,m,k)=True, f(n,m+1,k)=Falseとなるmを二分探索しよう。すると、(n+1)のうち後ろからk番目は、A(n)のm要素目の次に置くことになる。
int ask(vector<int> V) { cout<<"? "<<V.size(); FORR(a,V) cout<<" "<<a; cout<<endl; string s; cin>>s; return s=="Yes"; } int N; int C[505]; void solve() { int i,j,k,l,r,x,y; string s; cin>>N; vector<int> cur; for(i=1;i<=N;i++) { for(j=1;j<=N;j++) { vector<int> V(j,i); if(ask(V)) { C[i]=j; } else { break; } } if(C[i]==0) continue; if(cur.empty()) { FOR(j,C[i]) cur.push_back(i); continue; } vector<int> Q(C[i]+1); Q[0]=cur.size(); for(j=1;j<=C[i];j++) { int L=0,R=Q[j-1]+1; while(R-L>=2) { int M=(L+R)/2; vector<int> D; FOR(k,M) D.push_back(cur[k]); FOR(k,j) D.push_back(i); if(ask(D)) { L=M; } else { R=M; } } Q[j]=L; } FOR(j,C[i]) { x=Q[j+1]; cur.insert(cur.begin()+x,i); } } cout<<"!"; FORR(a,cur) cout<<" "<<a; cout<<endl; }
まとめ
シンプルな問題設定で良いね。