ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

洛谷P7843

2021-08-21 09:02:03  阅读:188  来源: 互联网

标签:lceil frac int P7843 read Subtask 洛谷 rceil


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\n");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\n",a,c);fflush(stdout);ans1=read();
  printf("! %d %d\n",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\n",ed[1],ed[2],ed[3],ed[4]);
    fflush(stdout);return 0;
  }
  printf("! %d %d\n",b,c);fflush(stdout);ans3=read();
  printf("! %d %d\n",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\n",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\n",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\times 10^4\)。

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

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

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

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

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

我们在数轴上找出每个长为 \(n\times \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\n",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\n",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\times 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\n",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\n",a,c);fflush(stdout);ans1=read();
printf("! %d %d\n",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\n",b,c);fflush(stdout);ans3=read();
  printf("! %d %d\n",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\n",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\times \frac{1}{2}^i\) 大小分的块可能使得 \(3,4\) 不在同一块中而 \(4,5\) 在同一块中,所以找完 \(1\) 到 \(4\) 的位置后再处理后面时 \(5\) 不会被处理到。

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

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

然后我就过了。

标签:lceil,frac,int,P7843,read,Subtask,洛谷,rceil
来源: https://www.cnblogs.com/KnightL/p/15168817.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有