0%

[XXXI POI] Permutacje

喵喵交互题。

题目链接。

题意

现在有一个长度为 的排列 (下标从 开始),你需要在若干次询问内问出它。

每一次询问需要给出一个排列 ,以及两个数字 ,交互库将会返回 中, 是否在同一个置换环。

,询问次数限制为 次。

题解

我还是第一次见到函数式交互题还需要我写主函数的。

感觉这题人类想不出来。

设 ,考虑能不能找到一个合适的 ,让 中恰好有一个置换环。

假设此时我们知道 中 不在一个置换环内,我们是否可以通过改变 来让这两个环合并呢。

我们直接让原来指向 的指向 ,原来指向 的指向 就好了。

这样我们就可以在 次操作以内让 内只有一个置换环。

考虑我们能不能知道,此时 长成什么样子。

考虑找两个点 ,如果我们让原来指向 的指向 ,原来指向 的指向 ,会发生什么。

可以发现,两个点中,更前面的一个,分裂出了一个环,并且这个环不包含 。

也就是说,我们可以使用一次询问,知道两个点谁在前面。

也就是说我们可以用 次询问知道 长什么样子。

因为 ,那么 ,我们就得到了 。

询问次数是 。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<bits/stdc++.h>
#include "perlib.h"
using namespace std;
template<typename A>
using vc=vector<A>;

int id[505];
int wh[505];
int q[505];
int a[505];
int n;
inline bool query(int a,int b)
{
vc<int>qq;
for(int i=0;i<n;i++) qq.push_back(q[i]);
return zapytaj(qq,a,b);
}
inline void rebuild()
{
for(int i=0;i<n;i++) wh[q[i]]=i;
}
void cdq(int l,int r)
{
if(l>=r) return ;
int mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
inplace_merge(id+l,id+mid+1,id+r+1,[](int i,int j)
{
int w1=wh[i],w2=wh[j];
q[w1]=j,q[w2]=i;
bool res=query(0,i);
q[w1]=i,q[w2]=j;
return !res;
});
}
int main()
{
n=daj_n();
for(int i=0;i<n;i++) id[i]=q[i]=wh[i]=i;
for(int i=1;i<n;i++) if(!query(0,i))
{
int w1=wh[0],w2=wh[i];
q[w1]=i,q[w2]=0;rebuild();
}

cdq(1,n-1);
for(int i=0;i<n;i++) a[id[i]]=id[i+1];

vc<int>ans;
for(int i=0;i<n;i++) ans.push_back(wh[a[i]]);
odpowiedz(ans);
return 0;
}
/*
g++ T1.cpp perlib.cpp -o T1 -std=c++14 -Wall
T1
5 100000
3 2 1 0 4
*/