题目描述
交互。每次可以询问一个排列,返回这个排列与答案排列相同位置的个数,求出这个答案排列。
数据范围
n ≤ 5 × 1 0 3 ; q u e r i e s ≤ 5 × 1 0 4 n \le 5 \times 10^3;queries \le 5 \times 10^4 n≤5×103;queries≤5×104
题解
考虑先求出一个错排,那答案会形如若干个置换环的形式。所以我们的目标是在 ( n − 1 ) × n 2 \frac{(n-1)\times n}{2} 2(n−1)×n 条边中,选出 n n n 条有效边。
考虑有效边的性质,即交换这个有效边的端点数值,询问结果会增加。所以对于每条边 ( i , j ) (i,j) (i,j) ,考虑按照 ( i + j ) % n (i+j)\%n (i+j)%n 分组,每组每条边的两个端点就互不相同,那我们可以用类似分治的方法求出哪些边是有效的,最后定向即可。
代码
#include "game.h"
using namespace std;
const int N=5005;
struct O{int x,y;};
vector<O>p[N],h;
vector<int>f[N],a;
int g[N<<2],S[N],cnt,lst,d[N];
bool vis[N];
bool J(int u){return d[u]<2;}
void add(int u,int v){d[u]++;f[u].push_back(v);}
void find(int k,int i,int l,int r){
if (l==r){
add(p[i][l].x,p[i][l].y);
add(p[i][l].y,p[i][l].x);
return;
}
int Ls=(k<<1),Rs=(Ls|1),mid=(l+r)>>1;
for (int j=l;j<=mid;j++)
swap(a[p[i][j].x],a[p[i][j].y]);
g[Ls]=count(a);g[Rs]=g[k]-g[Ls];
for (int j=l;j<=mid;j++)
swap(a[p[i][j].x],a[p[i][j].y]);
if (g[Ls]) find(Ls,i,l,mid);
if (g[Rs]) find(Rs,i,mid+1,r);
}
void dfs(int u){
vis[u]=1;S[++cnt]=u;
int z=f[u].size();
for (int i=0;i<z;i++)
if (!vis[f[u][i]]){
swap(a[u],a[f[u][i]]);
dfs(f[u][i]);
}
}
vector<int>guess(int n,int limit){
srand(233);a.resize(n);
for (int i=1;i<=n;i++) a[i-1]=i;
if (n==1) return a;
while(count(a))
random_shuffle(a.begin(),a.end());
for (int i=0;i<n;i++)
for (int j=i+1;j<n;j++)
p[(i+j)%n].push_back((O){i,j});
for (int z,i=0;i<n;i++){
z=p[i].size();if (!z) continue;h.clear();
for (int j=0;j<z;j++)
if (J(p[i][j].x) && J(p[i][j].y))
h.push_back(p[i][j]);
p[i]=h;z=p[i].size();if (!z) continue;
for (int j=0;j<z;j++)
swap(a[p[i][j].x],a[p[i][j].y]);
g[1]=count(a);
for (int j=0;j<z;j++)
swap(a[p[i][j].x],a[p[i][j].y]);
if (g[1]) find(1,i,0,z-1);
}
for (int i=0,v;i<n;i++)
if (!vis[i]){
cnt=0;dfs(i);v=count(a);
if (v==lst){
for (int j=cnt-1;j;j--)
swap(a[S[j+1]],a[S[j]]);
swap(a[S[1]],a[S[cnt]]);
for (int j=cnt-1;j>1;j--)
swap(a[S[j+1]],a[S[j]]);
}
lst+=cnt;
}
return a;
}