鱼和熊掌不可兼得

题目描述

交互。每次可以询问一个排列,返回这个排列与答案排列相同位置的个数,求出这个答案排列。

数据范围

$n \le 5 \times 10^3;queries \le 5 \times 10^4$

题解

考虑先求出一个错排,那答案会形如若干个置换环的形式。所以我们的目标是在 $\frac{(n-1)\times n}{2}$ 条边中,选出 $n$ 条有效边。

考虑有效边的性质,即交换这个有效边的端点数值,询问结果会增加。所以对于每条边 $(i,j)$ ,考虑按照 $(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;
}

猜你喜欢

转载自www.cnblogs.com/xjqxjq/p/12003486.html