1 条题解
-
0
Statement
给出 ,交互库初始拥有一个 的排列 。你每次可以调用一个函数 ,其中 的定义如下:
-
:此时交互库会返回 。
-
:此时交互库会返回 。
要求你在 次询问内还原该序列。。
Solution
讲一下思考过程。
称 可以打败 当且仅当 。
先考虑 的暴力如何做:对于每个 ,求出 。那么不难发现除了在排列中除了 的位置 都有 ,这是因为对于一个 ,若 均存在,那么此时 可以打败 ,而不能打败 ,两者算错的贡献恰好抵消了。而对于 ,我们会算出来 ,因此 中会有两个 ,多比较一次即可算出哪个是真正的 。对于 的同理算出哪个才是真正的 。那么此时我们有了 次数的做法。
我们考虑如何优化。发现本质上我们要求的东西就是一个值的排名,那么我们直接排个序,这样是不是就做完了?实则并非,考虑如下样例:
5 1 0 2 4 3手玩一下你会发现,在上面这个样例中,对于每一个 都可以打败 ,但是写出 数组发现完全是错的。而且假如你写了把 query 作为 cmp 传入 sort 中来排序,你会发现交上去 RE 了。这两个的具体原因都是,这些大小关系并不满足传递性,换句话说,假设 可以打败 就连边 ,则该图中有若干三元环 。
下面设排好序的(下标)序列为 ,此时满足 ,同时设 。
但是当我们研究完这个冲突的本质后,我们发现, 一定满足 或者 。对着这个东西再想一想,发现我们可以将 划分为若干个在下标上和值域上均连续的一些区间。形式化的,我们可以将 划分为若干个区间 ,且满足以下性质:
-
,有 。
-
,有 。
那么问题转化为求出所有的区间,考虑增量法。假设我们求出了上一个区间 ,则 ,然后我们对于 逐个判定 是否是当前区间的元素。但是这样不是很好做,不妨转化一下,判断 是否为当前区间的结尾。那么根据 以及 ,我们可以发现,当且仅当 时,。依赖这个性质,我们就可以求出除了第一个以外的区间了。
那么如何求出第一个区间呢?注意到我们只需要求出 即可,那么直接使用暴力即可。同时注意求出来的 的情况,由于我们的暴力不区分 ,此时需要再求出 才可以判断 到底是什么。
最劣查询次数是 。会略微会超过 一点点,但是由于取平均值的原因,依然可以获得 100 分。
100 pts 代码。
#include<bits/stdc++.h> #define ll long long #define pb emplace_back #define pir pair<int, ll> #define fi first #define se second #define inv(x) qpow(x, mod - 2) #define il inline #define mkpir make_pair #define ull unsigned long long #define umap unordered_map using namespace std; const int N = 1000 + 10, M = 2e5 + 10; const ll mod = 998244353; /* struct edge{ int v, next; }edges[M << 1]; int head[N], idx; void add_edge(int u, int v){ edges[++idx] = {v, head[u]}; head[u] = idx; } */ il ll qpow(ll x, ll y){ ll ret = 1; for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod; return ret; } il void chkmin(ll& x, ll y){if(y < x) x = y;} il void chkmax(ll& x, ll y){if(y > x) x = y;} il void chkmin(int& x, int y){if(y < x) x = y;} il void chkmax(int& x, int y){if(y > x) x = y;} il void chkmod(ll& x){x = (x + mod) % mod;} il void ADD(ll& x, ll y){x += y; (x >= mod) ? (x -= mod) : 0;} il void MUL(ll& x, ll y){x = x * y % mod;} //#define int long long extern "C" bool Query(int a, int b); int n, rk[N], id[N], tmp[N]; void mysort(int l, int r){ if(l >= r) return; int mid = (l + r >> 1), j = mid + 1, now = l; mysort(l, mid); mysort(mid + 1, r); for(int i = l; i <= mid; i++){ while(j <= r && Query(id[i], id[j])) tmp[now] = id[j], j++, now++; tmp[now] = id[i]; now++; } while(j <= r) tmp[now] = id[j], j++, now++; for(int i = l; i <= r; i++) id[i] = tmp[i]; } // can't distinguish(?) 0 / 1, n - 2 / n - 1 int getid(int x){ int gt = 0; for(int i = 0; i < n; i++) if(i != x) gt += Query(x, i); return gt; } vector<int> ans, res; void solve(int st){ int lstfir = 0, lst = st; for(int i = st; i < n; i++){ if(!Query(id[i], id[lstfir])){ int cnt = i; for(int j = lst; j <= i; j++) ans[j] = cnt, cnt--; lstfir = lst; lst = i + 1; } } } extern "C" std::vector<int> Solve(int N){ bool dbg = 0; n = N; ans.resize(n); res.resize(n); for(int i = 0; i < n; i++) id[i] = i; mysort(0, n - 1); for(int i = 0; i < n; i++) ans[i] = id[i]; if(dbg) return ans; // check //for(int i = 0; i < n; i++) cerr << id[i] << " "; cerr << "\n"; // for(int i = 0; i < n - 1; i++) assert(Query(id[i + 1], id[i])); // solve int a0 = getid(id[0]); if(a0 == 1){ if(getid(id[1]) == 1) ans[0] = 1, ans[1] = 0, solve(2); else ans[0] = 0, solve(1); } else if(a0 == n - 2){ if(getid(id[1]) == n - 2){ for(int i = 0; i < n; i++) ans[i] = n - i - 1; } else{ for(int i = 0; i < n - 1; i++) ans[i] = n - i - 1; ans[n - 1] = n - 1; } } else{ for(int i = 0; i <= a0; i++) ans[i] = a0 - i; solve(a0 + 1); } for(int i = 0; i < n; i++) res[id[i]] = ans[i]; return res; } -
- 1
信息
- ID
- 10161
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者