题解:[ABC355E] Guess the Sum
abc355_e 解题报告
前言
好玩的交互题!
思路分析
首先注意到题目要求最小化询问次数,感觉瓶颈不在于得出答案,而是如何合理的询问。
发现可以转化为图论问题。
具体地,我们对于每一组合法的询问 \([L,R)\),从 \(L\) 向 \(R\) 连一条边权为 \(1\) 的无向边,表示用 \(1\) 的代价可以拓展已知区间。
然后跑 \(L\) 到 \(R\) 的最短路,答案就是最少询问次数,询问就是最短路上的边。
注意从 \(L\) 到 \(R\) 的边是增加 \(ans\),从 \(R\) 到 \(L\) 的边是减少 \(ans\)。
有些细节,看代码吧。
复杂度不想算了。\(O(能过)\)。
代码实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,l,r,pw[21];
int head[2000005],nxt[8000005],target[8000005],tot;
void add(int x,int y){
tot++;
nxt[tot]=head[x];
head[x]=tot;
target[tot]=y;
}
void init(){
pw[0]=1;
for(int i=1;i<=n;i++){
pw[i]=pw[i-1]*2;
}
for(int i=0;i<=n;i++){
for(int j=0;j<=pw[n];j++){
//if(!pw[i]*j) cout<<pw[i]*j<<' '<<pw[i]*(j+1)<<endl;
if(pw[i]*(j+1)<=pw[n]) add(pw[i]*j,pw[i]*(j+1));
if(j && pw[i]*j<=pw[n]) add(pw[i]*j,pw[i]*(j-1));
}
}
}
int dis[2000005],pre[2000005];
queue<int> q;
void bfs(){
memset(dis,0x3f,sizeof(dis));
dis[l]=0;
q.push(l);
while(q.size()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=nxt[i]){
int y=target[i];
if(dis[y]>dis[x]+1){
dis[y]=dis[x]+1;
pre[y]=x;
q.push(y);
}
}
}
}
struct node{
int l,r;
};
vector<node> ans;
int cnt;
void dfs(int x){
cnt++;
if(x==l) return;
ans.push_back((node){pre[x],x});
dfs(pre[x]);
}
int sum=0,tp;
void out(int l,int r){
if(l>r) swap(l,r);
int i=__lg(r-l);
int j=l/pw[i];
cout<<"? "<<i<<' '<<j<<endl;
//cout<<pw[i]*j<<' '<<pw[i]*(j+1)-1<<endl;
}
void solve(){
for(int i=0;i<ans.size();i++){
out(ans[i].l,ans[i].r);
cin>>tp;
if(ans[i].l<ans[i].r) sum=(sum+tp)%100;
else sum=(sum-tp+100)%100;
}
cout<<"! "<<sum<<endl;
}
signed main(){
cin>>n>>l>>r;
r++;
init();
bfs();
dfs(r);
solve();
}
后记
喜欢好玩的题。