题解 差量

传送门

先有一个用两次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);
}
posted @ 2022-02-09 10:09  Administrator-09  阅读(0)  评论(0编辑  收藏  举报