正题
题目链接:https://www.luogu.com.cn/problem/P5208
题目大意
有一个长度为nnn的010101序列aaa,你知道里面有奇数个111还是偶数个111。你每次可以选择两个下标集合S/TS/TS/T询问集合SSS和集合TTT位置的数字和哪个更大。
交互库只会告诉你S≤TS\leq TS≤T或者S≥TS\geq TS≥T。
要求在所有询问集合大小之和不超过 500100500100500100 的情况下得到整个aaa序列。
1≤n≤1051\leq n\leq 10^51≤n≤105
解题思路
注意到数据中有一个点保证了aaa序列单调,我们先假设为单调不升。
此时有a1=1a_1=1a1=1,那么我们考虑二分,对于一个位置midmidmid,我们判是否amid+amid+1≤1a_{mid}+a_{mid+1}\leq 1amid+amid+1≤1,如果是,那么说明amid+1=0a_{mid+1}=0amid+1=0,否则amid+amid+1≥1a_{mid}+a_{mid+1}\geq 1amid+amid+1≥1就说明了amid=1a_{mid}=1amid=1。
这样我们同样每次能过缩小一半,但是最后会剩下两个数字,我们用奇偶性去判断。
那么对于一般的情况,我们考虑也去构造一个单调不升的序列,两个数字比不出东西,我们考虑拿三个数字去比较,假设我们现在有一个a≤ba\leq ba≤b。
我们拿a+ba+ba+b和ccc去比较:
- 若a+b≤ca+b\leq ca+b≤c,那么有a=0a=0a=0。
- 若a+b≥ca+b\geq ca+b≥c,那么有b≥cb\geq cb≥c,我们把bbb加入队列,这样加入队列的都是单调不升的。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int p[N],A[2],B[2];
int query(int *S,int nS,int *T,int nT);
void find_price(int task_id, int n, int k, int ans[]) {if(n==1){ans[0]=1;return;}if(task_id==3||n==2){A[0]=0;B[0]=n-1;if(!query(A,1,B,1))for(int i=1;i<=n;i++)p[i]=i-1;else for(int i=1;i<=n;i++)p[i]=n-i;int l=1,r=n-1;while(l<r){int mid=(l+r+1)>>1;A[0]=p[mid];A[1]=p[mid+1];B[0]=p[1];if(query(A,2,B,1))r=mid-1;else l=mid;}ans[p[l+1]]=(l&1)^k;for(int i=1;i<=l;i++)ans[p[i]]=1;}else{int x=0,y=1,tot=0;for(int i=2;i<n;i++){int z=i;A[0]=y;B[0]=z;//y>=zif(query(A,1,B,1))swap(y,z);A[0]=x;B[0]=y;B[1]=z;if(query(A,1,B,2)){p[++tot]=x;x=y;y=z;}else ans[z]=0;}A[0]=x;B[0]=y;if(query(A,1,B,1))p[++tot]=y;else p[++tot]=x,swap(x,y);for(int i=1;i<tot-i+1;i++)swap(p[i],p[tot-i+1]);int l=1,r=tot-1;while(l<r){int mid=(l+r+1)>>1;A[0]=p[mid];A[1]=p[mid+1];B[0]=p[1];if(query(A,2,B,1))r=mid-1;else l=mid;}for(int i=1;i<=l;i++)ans[p[i]]=1;y=p[l+1];A[0]=x;B[0]=y;//x>=yif(query(A,1,B,1))swap(x,y);A[0]=x;A[1]=y;B[0]=p[1];if(query(A,2,B,1))ans[y]=0;else ans[x]=1,l++,x=y;ans[x]=(l&1)^k;}return;
}