题解 P8568 [JRKSJ R6] func
好题,yzy 神中神 orz。
先来考虑 Sub2,也就是已知
接下来考虑 Sub3,也就是赛时的正解。Sub3 在
先解决第一个问题,对于某个
第二个问题是在找到一个合法的 -1 来排除拐点的影响,即询问 -1 可以直接确定拐点,否则两个方程是确定的:
不妨令
这里需要用到
令
赛后毒瘤 yzy 把次数卡到了
对于 Sub4 会遇到与之前同样的问题,也就是若
实现时还有个细节是如何尽量三等分区间
代码如下(码
//author:望远星
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define mk make_pair
#define sml(x,y) (x=min(x,y))
#define big(x,y) (x=max(x,y))
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define umap unordered_map
#define db double
#define fo(i,x,y) for(int i=(x);i<=(y);++i)
#define go(i,x,y) for(int i=(x);i>=(y);--i)
#define ptc putchar
#define emp emplace
#define re return
//#define int ll
using namespace std;
ll seed=chrono::system_clock::now().time_since_epoch().count();
mt19937 rnd(seed);
inline int rm(int x,int y){return (int)(rnd()%(y-x+1ll)+x);}
inline int read(){int ch=getchar(),x=0,f=1;while(!isdigit(ch)){if(ch==(int)('-'))f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void out(int *a,int l,int r){fo(i,l,r) cout<<*(a+i)<<' ';puts("");}
namespace func{
int k=100,b=11111,t=7;
ll F(int x){
if(t<x) return k*(x-1ll)+b;
return (ll)k*x+b;
}
//using func::F;
int cnt;
inline int ask(int l,int r,int p){
printf("? %d %d %d\n",l,r,p);
cnt++;
cout.flush();
return read();
//printf("%d\n",(F(l)+F(r))%p);
//if(F(l)==F(r)) return -1;
//return (F(l)+F(r))%p;
}
void answer(int x){
printf("! %d\n",x);
cout.flush();
if(!read()) exit(0);
//if(t!=func::t) exit(0);
// cout<<"cnt="<<cnt<<'\n';
}
}
using func::answer;
using func::ask;
const int P[]={49,47,45,43,41,37,31,29,25,23};
const int ni[]={25,24,23,22,21,19,16,15,13,12};
int n;
void solve(){
int L=1,R=read(),q=read(),pp=read();
if(pp==2){
int l=1,r=R,mid;
while(l<=r){
//printf("[%d,%d]\n",l,r);
if(r-l<=1){
answer(l);
return;
}
mid=l+((r-l)>>1);
//printf("mid=%d\n",mid);
int x=ask(1,mid,2);
if(x==-1){
answer(1);
return;
}
if((mid&1)^(x&1)) l=mid;
else r=mid;
}
re;
}
int p=0,k=0,b=0;
fo(i,0,9){
p=P[i];
int x=ask(1,2,p),y=ask(2,3,p);
if(x==-1){answer(1); re;}
if(y==-1){answer(2); re;}
if(x==y) continue;
k=(y-x+p)*ni[i]%p;
b=(x-3*k+3*p)%p;
break;
}
while(R-L>1){//对开区间 [l,R) 三等分
int x=(2ll*L+R)/3,y=(L+2ll*R)/3;
//printf("[%d,%d] %d,%d\n",L,R,x,y);
int w=ask(x,y,p);
if(w==-1){answer(x);re;}
int a0=(k*(x-2ll+y)+b)%p,a1=(k*(x-1ll+y)+b)%p,a2=(k*(x+0ll+y)+b)%p;
if(w==a0) R=x;
if(w==a1) L=x,R=y;
if(w==a2) L=y;
}
answer(L);
}
signed main(){
int T=read();
while(T--) solve();
return 0;
}