zoukankan      html  css  js  c++  java
  • 洛谷P7843

    Subtask 1

    注意到只有四个数。

    可以先通过一次操作二找出 (3,4) 的位置和 (1,2) 的位置。

    然后再分别用四次操作一,让 (3,4) 来对 (1,2) 取模确定四个位置。

    n=read();m1=read();m2=read();m3=read();
    if(n==4){
      printf("? 4 1 2 3 4 3
    ");fflush(stdout);
      k=read();a=read();b=read();
      for(int i=1;i<=4;i++)if(i!=a&&i!=b){if(!c)c=i;else d=i;}
      printf("! %d %d
    ",a,c);fflush(stdout);ans1=read();
      printf("! %d %d
    ",a,d);fflush(stdout);ans2=read();
      if(ans1+ans2==0) ed[b]=3,ed[a]=4;
      else{
        ed[b]=4,ed[a]=3;
        if(ans1) ed[c]=2,ed[d]=1;else ed[d]=2,ed[c]=1;
        printf("A %d %d %d %d
    ",ed[1],ed[2],ed[3],ed[4]);
        fflush(stdout);return 0;
      }
      printf("! %d %d
    ",b,c);fflush(stdout);ans3=read();
      printf("! %d %d
    ",b,d);fflush(stdout);ans4=read();
      if(ans3) ed[c]=2,ed[d]=1;else ed[c]=1,ed[d]=2;
      printf("A %d %d %d %d
    ",ed[1],ed[2],ed[3],ed[4]);
      fflush(stdout);return 0;
    }
    

    Subtask 2

    发现 (m_2=n),且 (m_3)(m_2^2)

    那么我们可以通过 (n) 次询问来确定每个数的位置。

    每次询问 (n) 个数,询问的上界减一,然后找出给的数中没有被确定位置的数即可。

    n=read();m1=read();m2=read();m3=read();
    if(n==500){
      for(int i=n;i>=1;i--){
        printf("? %d ",i);
        for(int j=1;j<=n;j++)
          if(!flag[j]) printf("%d ",j);
        printf("%d
    ",i);fflush(stdout);
        int x=read(),y=read();ed[y]=i,flag[y]=1;
      }
      printf("A ");
      for(int i=1;i<=n;i++) printf("%d ",ed[i]);
      fflush(stdout);return 0;
    }
    

    Subtask 3~6

    没看出来性质 A 可以怎么搞。

    看最下面的数据范围,发现 (m_1) 很大,(m_2) 很小,但是 (2^{17}>5 imes 10^4)

    这启发我们可以用类似倍增的做法来搞。

    可以发现,((lceilfrac{n}{2} ceil,n]) 中的每个数对 (lceilfrac{n}{2} ceil) 取模都可以得到不同的结果。

    所以,如果我们知道了 ([1,lceilfrac{n}{2} ceil]) 中每个数的位置,那么剩下的数位置可以通过不断进行操作一来确定。

    想要确定 ([1,lceilfrac{n}{2} ceil]),可以转化为知道了 ([1,lceilfrac{n}{4} ceil]) 来用同样的方法求 ((lceilfrac{n}{4} ceil,lceilfrac{n}{2} ceil]) 得到。

    所以我们可以通过递归分治的方式来处理。

    我们在数轴上找出每个长为 (n imes frac{1}{2}^{i}) 的段,然后处理这一段内的数的位置即可。

    void work(int l,int r,int cnt){
      int mid=(r+1)/2+1;all=0;
      if(l==r){for(int i=1;i<=n;i++)if(!pos[i]) pos[i]=cnt;return;}
      for(int i=1;i<=n;i++)if(!pos[i]) que[++all]=i;
      printf("? %d ",all);for(int i=1;i<=all;i++) printf("%d ",que[i]);
      printf("%d
    ",mid);fflush(stdout);int x=read();
      for(int i=1;i<=x;i++) pos[read()]=cnt;work(l,mid-1,cnt+1);
    }
    //下面是主函数内的部分
    n=read();m1=read();m2=read();m3=read();
    if(m2>=17){
      for(int i=1;i<=n;i++) num[i]=i;
      work(1,n,1);sort(num+1,num+n+1,cmp);
      ed[num[1]]=1;ed[num[2]]=2;int maxx,id=num[2];
      for(int i=3;i<=n;i++){
        if(pos[num[i]]!=pos[num[i-1]]) lst=id,maxx=id=0;
        printf("! %d %d
    ",num[i],lst);fflush(stdout);int x;
        ed[num[i]]=ed[lst]+(x=read());if(!x)ed[num[i]]=(ed[lst]<<1);
        if(maxx<ed[num[i]])maxx=ed[num[i]],id=num[i];
      }
      printf("A");for(int i=1;i<=n;i++) printf(" %d",ed[i]);
      fflush(stdout);return 0;
    }
    

    Subtask 7

    可以发现对于最后一组数据,(2^{15}le 5 imes 10^4<2^{16})。也就是说我们操作二理论上多进行了一次。

    想办法怎么减少次数。

    我们发现递归的时候是最后剩余了一个 (1) 然后退出的。此时我们可以找出 (1,2,3,4) 的位置。

    如果我们不用操作二找出最后一个 (1) 的位置,而是采用 Subtask 1 的方法来的话,理论上就可以使操作二控制在一个可行的范围内了。

    然后我们在 Subtask 3~6 的代码上套一个 Subtask 1 的代码就可以通过这个测试点。

    
    void work2(int l,int r,int cnt){
      int mid=(r+1)/2+1;all=0;
      if(mid==2){for(int i=1;i<=n;i++)if(!pos[i]) pos[i]=cnt;return;}
      for(int i=1;i<=n;i++)if(!pos[i]) que[++all]=i;
      printf("? %d ",all);for(int i=1;i<=all;i++) printf("%d ",que[i]);
      printf("%d
    ",mid);fflush(stdout);int x=read();
      for(int i=1;i<=x;i++) pos[read()]=cnt;work2(l,mid-1,cnt+1);
    }
    //下面是主函数内的部分
    for(int i=1;i<=n;i++) num[i]=i;
    work2(1,n,1);sort(num+1,num+n+1,cmp);
          
    int c=num[2],d=num[1],a=num[3],b=num[4];
    printf("! %d %d
    ",a,c);fflush(stdout);ans1=read();
    printf("! %d %d
    ",a,d);fflush(stdout);ans2=read();
    if(ans1+ans2!=0){
      ed[a]=3;ed[b]=4;
      if(ans1) ed[c]=2,ed[d]=1;else ed[d]=2,ed[c]=1;
    }
    else{
      ed[a]=4;ed[b]=3;
      printf("! %d %d
    ",b,c);fflush(stdout);ans3=read();
      printf("! %d %d
    ",b,d);fflush(stdout);ans4=read();
      if(ans3) ed[c]=2,ed[d]=1;else ed[c]=1,ed[d]=2;
    }
         
    int maxx,id;
    if(ed[num[3]]==4) id=num[3];
    else id=num[4];
    for(int i=5;i<=n;i++){
      if(pos[num[i]]!=pos[num[i-1]]) lst=id,maxx=id=0;
      printf("! %d %d
    ",num[i],lst);fflush(stdout);int x;
      ed[num[i]]=ed[lst]+(x=read());if(!x)ed[num[i]]=(ed[lst]<<1);
      if(maxx<ed[num[i]])maxx=ed[num[i]],id=num[i];
    }
    printf("A");for(int i=1;i<=n;i++) printf(" %d",ed[i]);
    fflush(stdout);return 0;
    

    但是这份代码又不能过掉 Subtask 3~4,因为在前面的小数据递归时,按 (n imes frac{1}{2}^i) 大小分的块可能使得 (3,4) 不在同一块中而 (4,5) 在同一块中,所以找完 (1)(4) 的位置后再处理后面时 (5) 不会被处理到。

    这里特判一下 (3,4) 是不是在一块就可以了。

    但是我是个懒逼,所以我把上面所有的代码全拼起来了。

    然后我就过了。

  • 相关阅读:
    Objective Evaluation Index of image
    MATLAB奔溃仅左上角显示关闭界面X
    论文修改指南之发表
    科研第一步:文献查找和下载
    实习项目1-串口IP升级调试
    FPGA驱动LCD显示红绿蓝彩条
    tw9912配置使用
    BT.656视频信号解码
    色彩空间转换 rgb转ycbcr422/ycbcr422转rgb
    xilinx VDMA IP核使用
  • 原文地址:https://www.cnblogs.com/KnightL/p/15168817.html
Copyright © 2011-2022 走看看