一、题目描述:

  这是一道交互题,你需要猜出一个 $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$ 的题真的蛮有意思的,其实没考什么算法,但是很有趣!加油!拜拜!

posted on 2023-05-05 19:26  trh0630  阅读(47)  评论(0)    收藏  举报