一、题目描述:
这是一道交互题,你需要猜出一个 $1$~$n$ 的全排列 $p_1,p_2,p_3...p_n$ 。
有 $t$ 组数据,每组数据有一个整数 $n$ 表示数组的大小。
假设一开始有一个只有 $n$ 个点,没有边的图。你有 $2\times n$次询问机会,两种询问方式:
第一种:$+$ $x$。表示将 $1~n$ 中所有 $1\leq x-i\leq n$ 的 $i$ 与 $n-i$ 连一条边。若连接成功,则回答 $1$,否则回答 $-1$。
第二种:$?$ $u$ $v$。表示询问从 $u$ 到 $v$ 的最小边数。若没有路径,则回答 $-1$。
你有两次猜测序列的机会,只要有一个对了就算过关。请保证你输入的 $x$ 在 $1$~$2\times n$ 之间,$u,v$ 在 $1$~$n$ 之间。
二、解题思路:
乍一看这一题还真没什么思路。但是我们的目的是构造一条链,这样能简化问题,方便猜测。
经过多次尝试后不难发现只需要 $+$ $n$ 再 $+$ $n+1$ 就可以构成一条链。
然后我们还剩下 $2\times n-2$ 次询问机会,我们需要找出这一条链的一个端点,再根据端点的询问就可以解决这个问题。
怎么求端点?感觉就跟求树的直径一样,随便找一个点,距离最远的一定就是端点。这里花掉 $n-1$ 次询问机会。
最后再挨个询问其他各个点与端点的距离,最后输出即可。注意最后输出了答案还要读入一个 $1$ ,表示你的答案正确。
但我的代码用的是别人的思路,一开始没想出来:
任意挑选两个点对另外 $n-1$ 个点求距离,因为是链,任两点距离唯一,所以可以直接算出。
设这两个点的位置为 $x_1,x_2$ (已知),要求的点位置为 $x$ (未知)。距离分别为 $dis_1,dis_2$。给一个简单证明:
$已知:\vert x-x_1 \vert=dis_1,\vert x-x_2 \vert=dis_,x_1!=x_2$
$x=x_1 \pm dis_1,x=x_2 \pm dis_2$
$Situation 1:$
$x_1-dis_1=x_2-dis_2,x_1+dis_1=x_2+dis_2$
$dis_1-dis_2=x_1-x_2,dis_1-dis_2=x_2-x_1$
$x_1-x_2=x_2-x_1<=>x_2=x_1$
$与x_1!=x_2矛盾,无解$
$Situation 2:$
$x_1-dis_1=x_2+dis_2,x_1+dis_1=x_2-dis_2$
$dis_1+dis_2=x_1-x_2,dis_1+dis_2=x_2-x_1$
$x_1-x_2=x_2-x_1<=>x_2=x_1$
$与x_1!=x_2矛盾,无解$
$综上所述,方程最多有一个解$
而这个题显然是有解的,所以有唯一解。
在计算的时候注意是否两个点求出的端点是同一个端点,要分类讨论。
三、完整代码:
1 #include<iostream> 2 #define N 1010 3 using namespace std; 4 int T,n,rans,a[N],vis[N]; 5 int ma[3],dis[3][N],ans[N]; 6 void add(int x) 7 { 8 cout<<"+ "<<x<<endl; 9 cin>>rans; 10 } 11 int query(int u,int v) 12 { 13 cout<<"? "<<u<<" "<<v<<endl; 14 cin>>rans; return rans; 15 } 16 void build_array() 17 { 18 for(int i=1;i<=n;i++) 19 vis[i]=0; 20 a[1]=(n+1)/2;vis[a[1]]=1; 21 for(int i=1;i<n;i++) 22 { 23 int t=n-a[i]; 24 if(vis[t]) t++; 25 vis[t]=1,a[i+1]=t; 26 } 27 } 28 void print(int d1,int d2) 29 { 30 ans[1]=a[d1];ans[2]=a[d2]; 31 for(int i=3;i<=n;i++) 32 { 33 int t11=d1+dis[1][i],t12=d1-dis[1][i]; 34 int t21=d2+dis[2][i],t22=d2-dis[2][i]; 35 if(t11==t21||t11==t22) ans[i]=a[t11]; 36 if(t12==t21||t12==t22) ans[i]=a[t12]; 37 } 38 for(int i=1;i<=n;i++) 39 cout<<ans[i]<<" "; 40 } 41 void solve() 42 { 43 cin>>n; 44 build_array(); 45 ma[1]=ma[2]=0; 46 add(n);add(n+1); 47 48 for(int i=1;i<=2;i++) 49 for(int j=1;j<=n;j++) 50 dis[i][j]=0; 51 52 for(int i=1;i<=2;i++) 53 for(int j=1;j<=n;j++) 54 if(i!=j) 55 { 56 dis[i][j]=query(i,j); 57 if(dis[i][j]>dis[i][ma[i]]) 58 ma[i]=j; 59 } 60 61 cout<<"! "; 62 if(ma[1]==ma[2]) 63 { 64 print(1+dis[1][ma[1]],1+dis[2][ma[2]]); 65 print(n-dis[1][ma[1]],n-dis[2][ma[2]]); 66 } 67 if(ma[1]!=ma[2]) 68 { 69 print(1+dis[1][ma[1]],n-dis[2][ma[2]]); 70 print(n-dis[1][ma[1]],1+dis[2][ma[2]]); 71 } 72 cout<<endl;cin>>rans; 73 } 74 int main() 75 { 76 cin>>T; 77 for(int i=1;i<=T;i++) 78 solve(); 79 return 0; 80 }
四、写题心得:
$Codeforces$ 的题真的蛮有意思的,其实没考什么算法,但是很有趣!加油!拜拜!
浙公网安备 33010602011771号