题面

这是一道交互题。

有一个未知的长度为

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(交互 / 构造)的更多相关文章

  1. B - Median Pyramid Easy 构造题

    B - Median Pyramid Easy Time limit : 2sec / Memory limit : 256MB Score : 400 points Problem Statemen ...

  2. $AT2163\ Median\ Pyramid\ Easy$ 构造

    正解:构造 解题报告: 传送门$QwQ$ 考虑如果有两个相邻格子是相同数字那么它们以上这两列就都会是这列数字(显然$QwQ$? 所以考虑只要构造出第$n-1$行的中心和中心右侧($or$左侧一样的$Q ...

  3. Gym - 100851J: Jump(交互+构造+(大胆瞎搞)))

    题意:给定长度为N的01串,现在让你猜这个串,猜的次数要不超过N+500次. 每次你猜一个串,系统会返回N/2,或N,或0.当且当有N/2个位置猜对,N个位置猜对,其他. 思路:因为信息不多,没有关联 ...

  4. Codeforces Round #669 (Div. 2) C. Chocolate Bunny (交互,构造)

    题意:有一个长度为\(n\)的隐藏序列,你最多可以询问\(2n\)次,每次可以询问\(i\)和\(j\)位置上\(p[i]\ mod\ p[j]\)的结果,询问的格式是\(?\ x\ y\),如果已经 ...

  5. Codeforces Round #356 (Div. 2)

    A. Bear and Five Cards time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  6. Kafka connect快速构建数据ETL通道

    摘要: 作者:Syn良子 出处:http://www.cnblogs.com/cssdongl 转载请注明出处 业余时间调研了一下Kafka connect的配置和使用,记录一些自己的理解和心得,欢迎 ...

  7. Educational Codeforces Round 65 (Rated for Div. 2) B. Lost Numbers

    链接:https://codeforces.com/contest/1167/problem/B 题意: This is an interactive problem. Remember to flu ...

  8. OrmLite使用小结(一)

    在使用OrmLite过程中,遇到了不少问题.鉴于中文文档比較少,看英文文档又不知道怎样看起.仅仅能遇到问题查找解决方法并整理出来,如有错误,希望能指正! ** 1.模糊条件查询 ** 使用条件查询时. ...

  9. 【Python】使用cmd模块构造一个带有后台线程的交互命令行界面

    最近写一些测试工具,实在懒得搞GUI,然后意识到python有一个自带模块叫cmd,用了用发现简直是救星. 1. 基本用法 cmd模块很容易学到,基本的用法比较简单,继承模块下的Cmd类,添加需要的功 ...

随机推荐

  1. dotnet core 也能协调分布式事务啦!

    2022 年 5 月 24 日,我们发布了 DBPack v0.1.0 版本,该版本主要 release 了分布式事务功能.在我们的规划里,DBPack 是要支持所有微服务开发语言协调分布式事务的,但 ...

  2. CentOS8设置国内镜像源(阿里云镜像)

    CentOS8设置国内镜像源(阿里云) 1.备份原有配置 [root@localhost ~]# mkdir /etc/yum.repos.d.bak [root@localhost ~]# mv / ...

  3. idea中一些常用的快捷键

    ctrl+shift+alt:多行操作psvm:生成main()方法:fori:生成for循环:Ctrl+Alt+v:自动补齐返回值类型ctrl+o:覆写方法ctrl+i:实现接口中的方法ctrl+s ...

  4. 一个紧张刺激的聊天器,要不要进来看看(Python UDP网络模型)

    先来哔哔两句:(https://jq.qq.com/?_wv=1027&k=QgGWqAVF) 互联网的本质是什么?其实就是信息的交换.那么如何将自己的信息发送到其他人的电脑上呢?那就需要借助 ...

  5. NC14683 储物点的距离

    NC14683 储物点的距离 题目 题目描述 一个数轴,每一个储物点会有一些东西,同时它们之间存在距离. 每次给个区间 \([l,r]\) ,查询把这个区间内所有储物点的东西运到另外一个储物点的代价是 ...

  6. 详解HashMap源码解析(上)

    jdk版本:1.8 数据结构: HashMap的底层主要基于数组+链表/红黑树实现,数组优点就是查询块,HashMap通过计算hash码获取到数组的下标来查询数据.同样也可以通过hash码得到数组下标 ...

  7. XML方式配置切面

    1. 概述  一个切面中需要包含什么,才能够作用到连接点?切面中是包含通知的,通知作用到连接点需要有切入点表达式. 除了使用AspectJ注解声明切面,Spring也支持在bean配置文件中声明切面. ...

  8. while循环--和do-while循环

    对于循环语句来说他会有一个回上去的箭头,这个回上去的箭头就形成了一个重复做的事情,那种重复做的事情我们就叫做循环 while循环 ~如果我们把while翻译作"当",那么一个whi ...

  9. SpringBoot到底是什么?

    摘要:Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程. 本文分享自华为云社区<SpringBoot到底是什么?如何理解p ...

  10. 彻底理解DDS(信号发生器)的fpga实现(verilog设计代码)

    DDS(Direct Digital Synthesis)是一种把一系列数字信号通过D/A转换器转换成模拟信号的数字合成技术. 它有查表法和计算法两种基本合成方法.在这里主要记录DDS查表法的fpga ...