P11083 题解
DaiRuiChen007 · · 题解
Problem Link
题目大意
交互器中有一个
x\in[1,n] ,每次询问k 可以得到[x\ge k] ,但交互器会撒至多一次谎,构造最优策略。数据范围:
n\le 3\times 10^4 。
思路分析
非常非常感谢 @zhouhuanyi 大佬的指导。
首先考虑如何维护答案,求出每个
我们的目标就是让
很显然
那么设
注意到 dp 值域很小,因此
时间复杂度
然后直接暴力打表记决策点,注意到本地静态空间无法超过 2G,因此用 std::vector 在代码里申请就能开得下。
我们并不需要记录所有决策点,首先
并且已知
那么我们只要打表出
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5000,M=17,MAXN=3e4+5,inf=1e9;
inline void chkmin(int &x,const int &y) { x=y<x?y:x; }
inline void chkmax(short &x,const short &y) { x=y>x?y:x; }
short f[M+1][N+5][N+5]; int lg[MAXN+5],up[M+1];
array <int,4> str[]={ /*all possible transition pos*/ };
void init(int n) {
for(int c=0;c<=M;++c) up[c]=min(1<<c,n);
memset(f,-0x3f,sizeof(f));
f[0][0][0]=1,f[0][0][1]=f[0][1][0]=0;
for(int c=0;c<=M;++c) {
for(int i=up[c];i>=0;--i) for(int j=up[c]-i;j>=0;--j) {
chkmax(f[c][i][j],f[c][i+1][j]),chkmax(f[c][i][j],f[c][i][j+1]);
}
if(c==M) return ;
for(int i=0;i<=up[c];++i) for(int j=0;i+j<=up[c];++j) if(f[c][i][j]>=0) {
if(f[c][j][i]>=0) chkmax(f[c+1][min(up[c+1]-i-j,(int)f[c][j][i])][i+j],f[c][i][j]); //split j
if(j<=(1<<c)) {
chkmax(f[c+1][min(up[c+1]-j,(1<<c)-j+i)][j],f[c][i][j]); //split i
chkmax(f[c+1][i][j],f[c][i][j]+(1<<c)-j); //split k
}
}
}
}
int dp(int i,int j,int k) {
if(i<=N&&j<=N) for(int c=0;c<=M;++c) if(f[c][i][j]>=k) return c;
return M+1;
}
int trs(int i,int j,int k) {
if(!j) return (i+k)/2;
if(i+j+k>N) {
for(auto it:str) if(it[0]==i&&it[1]==j&&it[2]==k) return it[3];
return -1;
}
int vl=dp(i,j,k)-1;
for(int p=1;p<=i;++p) if(vl==max(dp(i-p,j,k),lg[p+j])) return p;
for(int p=1;p<j;++p) if(vl==max(dp(p,j-p,k),dp(i,p,j-p))) return i+p;
for(int p=0;p<k;++p) if(vl==max(lg[j+k-p],dp(i,j,p))) return i+j+p;
return -1;
}
int o,st[MAXN],m,a[MAXN];
bool qry(int x) {
if(x>o) return 0;
cout<<"? "<<x<<endl; string _; cin>>_; return _=="Yes";
}
void solve(int n) {
m=0;
for(int i=1;i<=n;++i) st[++m]=i,a[i]=0;
while(m>1) {
int l=m,r=0;
for(int i=1;i<=m;++i) if(a[st[i]]==0) l=min(l,i),r=max(r,i);
int p=(l>r?m/2:trs(l-1,r-l+1,m-r))+1;
bool fl=qry(st[p]);
if(fl) for(int i=1;i<p;++i) ++a[st[i]];
else for(int i=p;i<=m;++i) ++a[st[i]];
int k=0;
for(int i=1;i<=m;++i) if(a[st[i]]<=1) st[++k]=st[i];
m=k;
}
cout<<"! "<<st[m]<<endl;
}
const int LIM[]={30000,23464,12286,6444};
signed main() {
for(int i=1;i<MAXN;++i) lg[i]=__lg(i-1)+1;
cin>>o;
init(min(N,o));
int n=3e4;
for(int p:LIM) if(o<=p) n=p;
if(o<=N) n=o;
while(true) {
solve(n);
string _; cin>>_; if(_=="Done") break;
}
return 0;
}