Codeforces 1028G - Guess the number(DP、交互)
涨见识了,交互题居然还可以这么玩……第一次做还真不一定能想得到。
首先此题最棘手的地方在于,如果你问的 \(k>x\) 就自动 gg。
考虑怎样处理这个限制。首先任意时刻我们确定的 \(x\) 的范围肯定是一个区间 \([l,r]\) 对吧。因为存在 \(x=l\) 的可能,所以在这个状态下我们只能问 \(k\) 不超过 \(\min(10^4,l)\) 的问题。因此我们考虑 DP。显然以 \(r\) 为状态,最少询问次数为值域有点太浪费,因此需要定义域与值域交换。这样我们可以得到一个大致的框架:\(dp_{i,j}\) 表示还有 \(i\) 次机会,目前确定的左端点 \(=j\),最多能确定的区间长度是多少。
考虑怎么转移,其实挺 trivial,就无脑根据 \(i-1\) 的 DP 值向后跳 \(j\) 步即可。最后我们发现 \(dp_{5,1}\) 就是题面中出现的那个奇怪的数 \(M\)。
求出 DP 值之后交互过程就很容易了。
总之就是用 DP 解决交互题的神仙思路。
const int MAXN=1e4;
const ll M=10004205361450474ll;
ll dp[7][MAXN+5];
void init_dp(){
for(int i=1;i<=MAXN;i++)dp[1][i]=i;
for(int i=2;i<=5;i++)for(int j=1;j<=MAXN;j++){
ll cur=j+dp[i-1][j]-1;
for(int k=1;k<=j;k++){
if(cur>=9999){cur+=(j-k+1)*(dp[i-1][MAXN]+1);break;}
cur++;cur=cur+dp[i-1][cur+1];
}dp[i][j]=cur-j+1;
}
}
void work(ll l,ll r,int x){
// printf("work %lld %lld %d\n",l,r,x);
int cnt=min(l,(ll)MAXN);
if(x==1){
printf("%lld ",r-l+1);
for(ll i=l;i<=r;i++)printf("%lld ",i);printf("\n");fflush(stdout);
exit(0);
}
vector<ll>vec;
for(ll cur=l+dp[x-1][cnt]-1,stp=1;stp<=cnt;stp++){++cur;vec.pb(cur);cur+=dp[x-1][min(cur+1,(ll)MAXN)];}
printf("%d",vec.size());
for(ll v:vec)printf(" %lld",v);printf("\n");fflush(stdout);
int t;scanf("%d",&t);
if(t==-1)exit(0);if(t==0)work(l,vec[0]-1,x-1);
else if(t==vec.size())work(vec.back()+1,r,x-1);
else work(vec[t-1]+1,vec[t]-1,x-1);
}
int main(){
init_dp();assert(dp[5][1]==M);
work(1,M,5);
return 0;
}