题解 差量
先有一个用两次2操作确定一个值的方法:
设 \(x, y\) 是已知的,那么可以两次查询得到当前数分别与 \(x, y\) 的差值
于是可以确定这个数
再有一个用一次2操作确定一个值的方法:
直接查出当前值与所有已知值的差值,然后可以缩小这个数所在的范围
于是枚举这个数是什么,可以利用得到的差值 check
然后正解:
- 可以查询差值相关的交互题确定具体元素的一个思路(?)
貌似普适性并不高(要求所有元素互不相同)
先查出全集的极差
然后二分一个前缀,看查询出的前缀中是否存在这个极差可以确定一个极值的位置 \(x\)
发现对于一个集合 \(s\),通过查询 \(s\) 和 \(s\cup\{x\}\) 可以得到 \(x\) 与每个数的差值
因为所有元素互不相同,所以每个差值都是唯一的
将下标二进制拆分,查询并存储所有下标的这一位是1的位置组成的集合与 \(x\) 的差值
然后一个差值是位置 \(i\) 对应的差值的条件是
这个差值在i为1的位对应的集合中都出现过 且 在i为0的位对应的集合中都没有出现过
需要 \(3\log n\) 次操作,特别地,算法时间复杂度大概是 \(O(n^2\log n)\) 级别的(?)
点击查看代码
#include "difference.h"
#include <bits/stdc++.h>
using namespace std;
#define N 300
#define pb push_back
namespace task1{
vector<int> s, ask, ret;
void find(int n, int m1, int m2) {
int t1, t2;
s.pb(t1=qry1(0)); s.pb(t2=qry1(1));
for (int i=2; i<n; ++i) {
int d1, d2;
ask.clear(); ask.pb(0); ask.pb(i); ret=qry2(ask); d1=ret[0];
ask.clear(); ask.pb(1); ask.pb(i); ret=qry2(ask); d2=ret[0];
if (t1+d1==t2+d2) s.pb(t1+d1);
else if (t1-d1==t2+d2) s.pb(t1-d1);
else if (t1+d1==t2-d2) s.pb(t1+d1);
else s.pb(t1-d1);
}
answer(s);
}
}
namespace task{
int s[N], x, y;
vector<int> ask, ret;
multiset<int> st[20];
int query1(int pos) {return qry1(pos-1);}
vector<int> query2(vector<int> t) {
for (auto& it:t) --it;
return qry2(t);
}
void find(int n, int m1, int m2) {
// cout<<"find"<<endl;
for (int i=1; i<=n; ++i) ask.pb(i);
ret=query2(ask);
int dlt=*max_element(ret.begin(), ret.end());
int l=1, r=n, mid;
while (l<=r) {
mid=(l+r)>>1;
ask.clear();
for (int i=1; i<=mid; ++i) ask.pb(i);
ret=query2(ask);
if (!ret.size() || *max_element(ret.begin(), ret.end())!=dlt) l=mid+1;
else r=mid-1;
}
y=l;
// cout<<"y: "<<y<<endl;
int lim=0;
while (n>1<<(lim+1)) ++lim;
// cout<<"lim: "<<lim<<endl;
// cerr<<"lim: "<<lim<<endl;
for (int i=0; i<=lim; ++i) {
ask.clear();
for (int j=1; j<=n; ++j) if (j!=y && j&(1<<i)) ask.pb(j);
ask.pb(y);
ret=query2(ask);
for (auto it:ret) st[i].insert(it);
ask.pop_back();
ret=query2(ask);
for (auto it:ret) {
assert(st[i].find(it)!=st[i].end());
st[i].erase(st[i].find(it));
}
}
// cerr<<"pos1"<<endl;
for (int i=1; i<=n; ++i) if (i!=y) {
// cerr<<"i: "<<i<<endl;
multiset<int> tem;
bool any=0;
for (int j=0; j<=lim; ++j) if (i&(1<<j)) {
// cerr<<"j: "<<j<<endl;
if (!any) tem=st[j], any=1;
else {
// cerr<<"pos5"<<endl;
int cnt=0;
auto it=tem.begin();
while (it!=tem.end()) {
if (st[j].find(*it)==st[j].end()) {
it=tem.erase(it);
}
else ++it;
}
// for (auto it:tem) {
// cerr<<"it: "<<endl;
// if (++cnt>100) cerr<<"loop"<<endl;
// if (st[j].find(it)==st[j].end() && tem.find(it)!=tem.end()) {
// cerr<<"erase"<<endl;
// assert(tem.find(it)!=tem.end());
// tem.erase(tem.find(it));
// }
// }
// cerr<<"out"<<endl;
}
}
// cerr<<"pos4"<<endl;
for (int j=0; j<=lim; ++j) if (!(i&(1<<j))) {
for (auto it:st[j])
if (tem.find(it)!=tem.end())
tem.erase(it);
}
assert(tem.size()==1);
s[i]=*tem.begin();
}
// cerr<<"pos2"<<endl;
for (int i=1; i<=n; ++i) if (s[i]>s[x]) x=i;
// cout<<"x: "<<x<<endl;
s[x]=query1(x); s[y]=query1(y);
if (s[x]<s[y]) {for (int i=1; i<=n; ++i) if (i!=x&&i!=y) s[i]=s[y]-s[i];}
else {for (int i=1; i<=n; ++i) if (i!=x&&i!=y) s[i]=s[y]+s[i];}
// cerr<<"pos3"<<endl;
ret.clear();
for (int i=1; i<=n; ++i) ret.pb(s[i]);
// cerr<<"ret: "; for (int i=1; i<=n; ++i) cerr<<s[i]<<' '; cerr<<endl;
answer(ret);
}
}
void find(int n, int m1, int m2) {
// task1::find(n, m1, m2);
task::find(n, m1, m2);
}