abc355e 題解

yhddd發表於2024-06-06

abc355e

思路

WC2024T3 中知道一個技巧:如果知道區間 \([l,r]\) 的和就連邊 \(l\to r+1\),那麼想推出 \([L,R]\) 的區間和就要求 \(L\)\(R+1\) 聯通。

按題意把符合要求的邊連上,設邊權為 \(1\) 跑 bfs,求出 \(L\)\(R+1\) 的最短路並記錄路徑上的點,就可以得到要詢問的區間。

因為是從小往大推,對於最短路上的邊 \(u\to v\),如果 \(u<v\) 就把區間和加上,否則減去。

code

int n,a,b;
int dis[maxn],pre[maxn];
vector<pii> ans;int res;
queue<int> q;
signed main(){
	n=read();a=read();b=read();
	mems(dis,0x3f);dis[a]=0;q.push(a);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=1;i<=(1<<n);i<<=1){
			int v=u+i;
			if(u%i==0&&v<=(1<<n)){
				if(dis[v]>dis[u]+1)dis[v]=dis[u]+1,pre[v]=u,q.push(v);
			}
			v=u-i;
			if(v%i==0&&v>=0){
				if(dis[v]>dis[u]+1)dis[v]=dis[u]+1,pre[v]=u,q.push(v);
			}
		}
	}
	// cout<<dis[0]<<" "<<dis[8]<<"\n";
	int u=b+1;
	while(u!=a){
		ans.push_back({pre[u],u});
		u=pre[u];
	}
	for(auto [l,r]:ans){
		int i=log2(abs(l-r)),j=min(l,r)/abs(l-r);
		if(l<r){
			printf("? %lld %lld\n",i,j);fflush(stdout);
			res+=read();
		}
		else{
			printf("? %lld %lld\n",i,j);fflush(stdout);
			res-=read();
		}
	}
	res%=100,res+=100,res%=100;
	printf("! %lld\n",res);fflush(stdout);	
}