• Bzoj 4371: [IOI2015]sorting排序 二分


    题目

    似乎很久没写题解了...

    这题是校里胡策的时候的题,比赛因为评测机有点慢+自己代码常数大没快读...被卡t了,但是bzoj上还是A了的...,因为bzoj时限比较宽可以不卡常。

    题解:

    首先可以发现答案与操作顺序是无关的,也就是说,可以钦定答案就是x次操作,然后让先手的x次先全换了,然后再考虑我要怎么换,才能在最少次数内换成升序。

    于是就可以直接枚举答案x,然后判一下x是否可行。

    考虑如何判断,问题会变成,给定一个a序列,每次可以交换两个数,问最少交换多少次可以换成升序,也就是变成a[i]=i。

    这是一个经典问题,考虑 i 必须换到 a[i] 的位置,于是就直接一直跳就好了,最后会变成若干个环。

    一个环中如果有n个数,那么必须需要也只需要 n-1 次就可以换成 a[i]=i 的情况。

    因此最少的交换次数就是每个环大小-1加起来,可以在 O(n) 的时间复杂度下完成判断。

    那么枚举答案+判断答案是 O(n^2) 的,没得聊。

    其实答案是可以二分的,满足二分性质。

    证明:即证明如果答案为x可行,那么答案为x+1也必然可行,如果x可行,那么我花x次操作变成升序,然后第x+1次操作,先手怎么换,我就再换回去,序列依旧是升序的。

    那么效率就是 O(nlogn) 的

    虽然答案与顺序无关,但是操作方案是和顺序有关的。

    转换一下思路,交换两个数,可以理解成两个位置交换,但是其实也可以理解成两个数字交换。

    而位置和顺序有关,因为先手换完之后我本来想换的位置就会变了。

    但是数字和顺序是没关系的,所以我记录一下我环中交换的那些数。

    然后按题意模拟,每次维护一下now[i]表示 i 这个数现在的位置,于是就变得很简单了...

    注意一下两个人都操作一次才算完,不能先手操作完后是升序的我就不操作了。

    所以如果可以不操作的,要拿 0 0补满

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define maxn 200050
     4 using namespace std;
     5 int n;
     6 int v[maxn],now[maxn];
     7 int a[maxn],b[maxn],x[maxn*3],y[maxn*3];
     8 struct qnode{
     9     int x,y;
    10 }q[maxn*3];
    11 int check(int m){
    12     for (int i=0;i<n;i++)
    13     b[i]=a[i],v[i]=0;
    14     for (int i=1;i<=m;i++)
    15     swap(b[x[i]],b[y[i]]);
    16     int need=0;
    17     for (int i=0;i<n;i++)
    18     if (!v[i]){
    19         int x=i;
    20         while (!v[x]){
    21             v[x]=1;
    22             if (!v[b[x]]) {
    23                 q[++need].x=b[x];
    24                 q[need].y=b[b[x]];
    25             }
    26             x=b[x];
    27         }
    28     }
    29     return need;
    30 }
    31 int main(){
    32 //    freopen("game.in","r",stdin);
    33 //    freopen("game.out","w",stdout);
    34     scanf("%d",&n);
    35     for (int i=0;i<n;i++)
    36     scanf("%d",&a[i]),now[a[i]]=i;
    37     int Q;
    38     scanf("%d",&Q);
    39     for (int i=1;i<=Q;i++)
    40     scanf("%d%d",&x[i],&y[i]);
    41     int l=0,r=Q;
    42     while (l<=r){
    43         int m=(l+r)>>1;
    44         if (check(m)>m) l=m+1;else r=m-1;
    45     }
    46     int need=check(l);
    47     printf("%d
    ",l);
    48     for (int i=1;i<=need;i++){
    49         swap(a[x[i]],a[y[i]]);
    50         now[a[x[i]]]=x[i];
    51         now[a[y[i]]]=y[i];
    52         printf("%d %d
    ",now[q[i].x],now[q[i].y]);
    53         swap(a[now[q[i].x]],a[now[q[i].y]]);
    54         now[a[now[q[i].x]]]=now[q[i].x];
    55         now[a[now[q[i].y]]]=now[q[i].y];
    56     }
    57     for (int i=need+1;i<=l;i++)
    58     printf("0 0
    ");
    59     return 0;
    60 }
    4371
  • 相关阅读:
    web.xml+spring mvc基本配置
    REST服务安全-双向认证
    thymeleaf 配置
    jenkins
    linux下ssh/scp无密钥登陆方法
    java编译 Error: Could not find or load main class java执行包main方法
    文本按列导入excel
    linux脚本-判断进程是否存在,从而可以做预警处理..
    Linux中顿号
    >/dev/null 2>&1
  • 原文地址:https://www.cnblogs.com/Bunnycxk/p/10692340.html
Copyright © 2020-2023  润新知