神题,目前学 OI 以来遇到过的最强的题。
简要题意:一个值域为 \([0,1]\) 的长度为 \(n\) 的序列 \(a\) 被隐藏了起来,初始时我们知道 \(a\) 中至少有一个 \(1\) 与 \(1\) 的奇偶性,每次可以询问两个集合 \(S,T\),交互器会返回是 \(S\ge T\) 还是 \(T\ge S\),一次询问的代价是 \(T\) 的大小与 \(S\) 的大小之和,你需要使得总代价尽可能的小,大概的范围是 \(5n+6\log n\)。
Solution
算法 1
考虑每个人大力两两比较一次,那么每个人也就比较 \(n-1\) 次,然后可以把比较胜利的次数排个序,为 \(1\) 元素的胜利次数一定大于为 \(0\) 元素的胜利次数。
那么一定存在一个分界点,分界点左右分别是 \(0,1\),不难发现此时最大值一定是排序出的最大值,那么其答案一定为 \(1\)。
接下来将相邻的两个元素绑在一起作为 \(T\) 与这个 \(1\) 作为 \(S\) 问一下,那么问出来的一定是一段 \(T\) 大一段 \(S\) 大,但是 \(0,1\) 相邻的情况不好搞,而且 \(S,T\) 也会相邻,我们无法确定相邻的 \(S,T\) 哪个对应着 \(0,1\) 相邻,所以需要依赖奇偶性判断一下。
此时相邻问其实可以使用二分优化来确定分界点。
时间复杂度 \(O(n^2)\),询问费用 \(n\times (n-1)+3\log n\),期望得分 \(40\) 分。
大力两两比较有必要吗?直接快排不好吗?
时间复杂度 \(O(n\log n)\),询问费用 \(n\log n+3\log n\),期望得分 \(52\) 分。
算法 2
注意到本题只有 \(0,1\) 两种权值,不如思考,排序究竟有没有用。
尝试找出一个最大值 \(q\),显然这个值可以在 \(2n\) 的代价内找出,接下来利用这个元素来推断。
如果当前只剩下一个元素没有被推出,那么可以直接依赖奇偶性推断,考虑其他元素,任选两个元素 \(x,y\),假设 \(x\ge y\),接下来询问 \(\{x,y\}\) 与 \(\{q\}\):
- 如果 \(\{x,y\}\ge \{q\}\),那么 \(x,y\) 至少一个为 \(1\),又有 \(x\ge y\),那么 \(x=1\);
- 否则,那么 \(x,y\) 至少一个为 \(0\),又有 \(x\ge y\),那么 \(y=0\)。
不难发现每次询问至少会确定一个,把确定的换掉就可以了,时间复杂度 \(O(n)\),询问费用 \(2n+5n=7n\),期望得分 \(69\) 分。
算法 3
不难发现确定元素的步骤似乎无法优化,那么最大值可不可以删去呢?答案是可以的。
取一个元素作为基准值,接下来考虑如果其是 \(0\) 如何处置。
任取元素 \(a,b,c\),同样假设 \(b\ge c\),询问 \(\{b,c\}\) 与 \(\{a\}\):
- 如果 \(\{b,c\}\le \{a\}\),那么 \(b,c\) 必然不全是 \(1\),那么 \(c=0\),换掉 \(c\);
- 否则,如果 \(a\) 是 \(1\),则 \(b\) 一定是 \(1\),也就意味着 \(b\ge a\),用 \(b\) 替换 \(a\),并换一个新 \(b\) 上来。
结束上述过程之后,我们也就可以确定若干个元素,确定一条 \(x_1\ge x_2\ge x_3\ge \ldots \ge x_k\) 的链,和一个未知元素 \(p\)。
不难发现 \(x_k\) 和 \(p\) 中必有一个为 \(1\),如果 \(p\) 位置是 \(1\),那么可以把 \(p\) 接在 \(x_k\) 后面。
那么链上的节点的分界点可以使用算法 1 确定,再利用奇偶性确定 \(p\) 与分界点元素。
时间复杂度 \(O(n)\),询问费用 \(O(5n+3\log n)\),期望得分 \(100\) 分。
Code
void chkmax(int &x,int y) {if(x<y) x=y;}
void chkmin(int &x,int y) {if(x>y) x=y;}
const int N=1e5;
int id[N+10],tot;
int query_(int x,int y) {
int S[1]={x},T[1]={y};
return query(T,1,S,1);
}
int query__(int a,int x,int y) {
int S[1]={a},T[2]={x,y};
return query(T,2,S,1);
}
void find_price(int myd,int n,int K,int ans[]) {
if(n==1) {ans[0]=1;return;}
if(n==2) {
if(K%2==0) ans[0]=1,ans[1]=1;
else {
bool res=query_(0,1);
ans[res]=1,ans[res^1]=0;
}
return;
}
if(myd==3) {
for(int i=0;i<n;i++) id[i]=i;
if(!query_(0,n-1)) reverse(id,id+n);
int L=0,R=n;
while(L+1<R) {
int mid=(L+R)>>1;
if(mid<n-1&&!query__(id[0],id[mid],id[mid+1])) L=mid;
else R=mid;
}
int t=(L+1-K)&1;
ans[id[R]]=t;
for(int i=0;i<=L;i++) ans[id[i]]=1;
return;
}
tot=0;
int a=0,x=1,y=2,p1;
id[tot++]=p1;
for(int i=3;i<=n;i++) {
if(query_(x,y)) swap(x,y);
if(query__(a,x,y)) x=i;
else id[tot++]=a=y,y=i;
}
reverse(id,id+tot);
x=x<n?x:y;
int mx=query_(id[0],x)?id[0]:x;
int L=0,R=tot;
while(L+1<R) {
int mid=(L+R)>>1;
if(mid<tot-1&&!query__(mx,id[mid],id[mid+1])) L=mid;
else R=mid;
}
int t=(L+1-K)&1;
if(t) {
if(query_(id[R],x)) ans[id[R]]=1;
else ans[x]=1;
}
else if(!query__(mx,id[R],x)) ans[id[R]]=ans[x]=1;
for(int i=0;i<=L;i++) ans[id[i]]=1;
return;
}