• 鱼和熊掌不可兼得


    题目描述

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

    数据范围

    $n le 5 imes 10^3;queries le 5 imes 10^4$

    题解

    考虑先求出一个错排,那答案会形如若干个置换环的形式。所以我们的目标是在 $frac{(n-1) imes 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;
    }
  • 相关阅读:
    return和yield的区别
    基本装饰器
    javascript实例:两种方式实现tab栏选项卡
    javascript实例:路由的跳转
    javascript实例:点亮灯泡
    标签页QTabWidget
    主窗口QMainWindow和启动画面
    各种对话框
    列表视图QlistView
    拆分窗口QSplitter
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/12003486.html
Copyright © 2020-2023  润新知