[CF1526F] Median Queries(交互 / 构造)

2022-10-22,,,

题面

这是一道交互题。

有一个未知的长度为

N

\tt N

N 的排列

P

\tt P

P,已知

P

1

<

P

2

\tt P_1 < P_2

P1​<P2​ 。

每次询问格式为 “

?

a

b

c

\tt?~a~b~c

? a b c ”,返回值为三元组

{

P

a

P

b

,

P

b

P

c

,

P

a

P

c

}

\tt \{|P_a-P_b|,|P_b-P_c|,|P_a-P_c|\}

{∣Pa​−Pb​∣,∣Pb​−Pc​∣,∣Pa​−Pc​∣} 的中位数。

在至多

2

n

+

420

\tt 2n+420

2n+420 次询问后,输出排列

P

\tt P

P,格式为 “

!

P

1

P

2

P

N

\tt!~P_1~P_2~\dots~P_N

! P1​ P2​ … PN​ ”。

然后每组数据会返回一个数,为

1

\tt1

1 则答案正确,为

1

\tt-1

−1 则错误,不要忘了输入这个数。

一共

t

(

1

t

1000

)

\tt t(1\leq t\leq1000)

t(1≤t≤1000) 组数据,保证

20

N

1

0

5

,

N

1

0

5

20\leq N\leq 10^5,\tt \sum N\leq 10^5

20≤N≤105,∑N≤105。

题解

有两种各占优势的做法。


Solution #1

首先,返回该三元组的中位数,相当于:若

P

a

>

P

b

>

P

c

\tt P_a>P_b>P_c

Pa​>Pb​>Pc​,则返回

max

{

P

a

P

b

,

P

b

P

c

}

\tt\max\{P_a-P_b,P_b-P_c\}

max{Pa​−Pb​,Pb​−Pc​}。

有了这个简化,才能更好地继续推。

该解法的核心在于:如果已经知道了

1

\tt1

1 和

2

\tt2

2 的位置(假设为

P

A

,

P

B

\tt P_A,P_B

PA​,PB​),那么可以在

n

2

\tt n-2

n−2 次操作后,确定整个排列。也就是分别询问

{

A

,

B

,

i

}

\tt\{A,B,i\}

{A,B,i},所得值(

q

u

e

r

y

(

A

,

B

,

i

)

\tt query(A,B,i)

query(A,B,i))

+

2

\tt+2

+2 便是

P

i

\tt P_i

Pi​ 了。

问题是怎么获得

1

\tt1

1 和

2

\tt2

2 的位置,或者说,也可以获得

N

\tt N

N 和

N

1

\tt N-1

N−1 的位置,总之通过

P

1

<

P

2

\tt P_1<P_2

P1​<P2​ 确定最终答案。

经过一段尝试,我们发现,如果找到这么两个位置

a

\tt a

a 和

b

\tt b

b ,满足

P

a

P

b

N

4

3

\tt|P_a-P_b|\leq \frac{N-4}{3}

∣Pa​−Pb​∣≤3N−4​ ,处理出所有的

q

[

i

]

=

q

u

e

r

y

(

a

,

b

,

i

)

\tt q[i]=query(a,b,i)

q[i]=query(a,b,i) ,那么

q

[

i

]

\tt q[i]

q[i] 最大的

i

\tt i

i 就是

1

\tt1

1 或者

N

\tt N

N 的位置,

q

[

i

]

\tt q[i]

q[i] 严格次大的

i

\tt i

i 就是

2

\tt2

2 或者

N

1

\tt N-1

N−1 的位置。这样只需要多进行

N

2

\tt N-2

N−2 次操作。

那么,在剩下的

424

\tt424

424 次操作内,我们需要解决两个问题:

找到这样的位置

a

,

b

\tt a,b

a,b 。
找到

1

\tt1

1 和

2

\tt2

2 的或者

N

\tt N

N 和

N

1

\tt N-1

N−1 的位置

A

,

B

\tt A,B

A,B 。

找 a b:

我们可以证明:对于任意

13

\tt13

13 个位置,总存在

3

\tt3

3 个位置

x

,

y

,

z

\tt x,y,z

x,y,z ,满足

q

u

e

r

y

(

x

,

y

,

z

)

n

4

6

\tt query(x,y,z)\leq\frac{n-4}{6}

query(x,y,z)≤6n−4​(

max

{

P

x

P

y

,

P

y

P

z

,

P

x

P

z

}

n

4

3

\tt\Rightarrow \max\{|P_x-P_y|,|P_y-P_z|,|P_x-P_z|\}\leq\frac{n-4}{3}

⇒max{∣Px​−Py​∣,∣Py​−Pz​∣,∣Px​−Pz​∣}≤3n−4​)。

可以用反证法进行证明,即假设存在某

13

\tt13

13 个位置,满足

{

x

,

y

,

z

}

,

max

{

P

x

P

y

,

P

y

P

z

,

P

x

P

z

}

>

n

4

3

\tt\forall \{x,y,z\},\max\{|P_x-P_y|,|P_y-P_z|,|P_x-P_z|\}>\frac{n-4}{3}

∀{x,y,z},max{∣Px​−Py​∣,∣Py​−Pz​∣,∣Px​−Pz​∣}>3n−4​ 。贪心地填数进去,你就会发现,刚好到

13

\tt13

13 个时,你就满足不了条件了。所以这种情况不存在,假设不成立。

找这

3

\tt3

3 个位置,需要枚举

(

13

3

)

=

286

\tt{13\choose3}=286

(313​)=286 种情况,完全足够。

找到这样的

3

\tt3

3 个位置时,任选两个就可以作

a

,

b

\tt a,b

a,b 了。

找 A B:

我们会发现,假如你确定了某个

q

[

i

]

\tt q[i]

q[i] 最大的

A

\tt A

A 作为

1

\tt1

1 的位置,那么还是有两个位置可能为

2

\tt2

2 ,一个是真

2

\tt2

2 ,一个是

N

1

\tt N-1

N−1 ,令其分别为

B

1

,

B

2

\tt B_1,B_2

B1​,B2​ ,我们稍稍归纳一下就会发现:

q

u

e

r

y

(

A

,

B

1

,

a

)

q

u

e

r

y

(

A

,

B

2

,

a

)

q

u

e

r

y

(

A

,

B

1

,

b

)

q

u

e

r

y

(

A

,

B

2

,

b

)

{\tt query(A,B_1,a)\leq query(A,B_2,a)}\\ {\tt query(A,B_1,b)\leq query(A,B_2,b)}

query(A,B1​,a)≤query(A,B2​,a)query(A,B1​,b)≤query(A,B2​,b)

而且最多只有一个取等。因此便可以把

B

1

\tt B_1

B1​ 判断出来了。

CODE

#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) ((-x) & (x))
#define INF 0x3f3f3f3f
LL read() {
LL f=1,x=0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
int n,m,i,j,s,o,k;
inline int ask(int a,int b,int c) {
cout<<"? "<<a<<" "<<b<<" "<<c<<endl;
int as; cin>>as; return as;
}
int a[MAXN],p[MAXN],ar[MAXN];
vector<int> bu[MAXN];
int main() {
srand(time(0));
int T;cin>>T;
while(T --) {
cin>>n;
for(int i = 1;i <= n;i ++) bu[i].clear(),a[i] = 0,ar[i] = i;
random_shuffle(ar + 1,ar + 1 + n);
int A = 1,B = 2,C = 3,as,flag1 = 0;
for(A = 1;A <= 11;A ++) {
for(B = A+1;B <= 12;B ++) {
for(C = B+1;C <= 13;C ++) {
if((as=ask(A,B,C)) <= ((n-4)/6)) {
flag1 = 1; break;
}
}
if(flag1) break;
}
if(flag1) break;
}
int mi = n+1,ct = 1,ct2 = 0,ma = 0;
for(int i = 1;i <= n;i ++) {
if(i != A && i != B) {
a[i] = ask(A,B,i);
if(a[i] < mi) mi = a[i],ct = 1;
else if(a[i] == mi) ct ++;
if(a[i] == 2) ct2 ++;
ma = max(ma,a[i]);
bu[a[i]].push_back(i);
}
}
int H = bu[ma][0],H2 = bu[ma-1][0];
if((int)bu[ma-1].size() > 1) {
int H3 = bu[ma-1][1];
if(ask(H,H2,A) <= ask(H,H3,A) && ask(H,H2,B) <= ask(H,H3,B)) {
H2 = H2;
}
else H2 = H3;
}
p[H] = 1; p[H2] = 2;
for(int i = 1;i <= n;i ++) {
if(i != H && i != H2) {
p[i] = 2 + ask(H,H2,i);
}
}
if(p[1] > p[2]) {
for(int i = 1;i <= n;i ++) p[i] = n-p[i]+1;
}
cout<<"!";
for(int i = 1;i <= n;i ++) cout<<" "<<p[i];
cout<<endl;
int AC; cin>>AC;
}
return 0;
}

Solution #2

非常精妙的做法,不愧是

O

n

e

I

n

D

a

r

k

\tt OneInDark

OneInDark !

主要是选择两对数来进行序列

q

\tt q

q 的确定,通过

q

\tt q

q 和

q

\tt q'

q′ 确定原序列。这里笔者就不展开了。原博客还是写了满大页的。

[CF1526F] Median Queries(交互 / 构造)的相关教程结束。

《[CF1526F] Median Queries(交互 / 构造).doc》

下载本文的Word格式文档,以方便收藏与打印。