Solutions - NOISG 2019 重现赛
好的。
T1
主观难度:【0】
说真的我现在已经忘了 T1 是什么了,只记得很简单一眼了。
T2
主观难度:【0】
同。
T3
主观难度:【1】
我们注意到本题可以构建费用流模型,于是证明了题目的凸性。
然后 wqs 做就行了。
T4
主观难度:【0】
简单贪心,场上一眼秒了。
T5
主观难度:【3+】
草。
在 github 上能找到官方题解,但是可读性不太好,应该不是人能读的。让 A.I. 搞了一下发现 A.I. 也读不懂。
以下称“盒子”为“集合”。
Subtask 3
Subtask 3 很爽的一点在于它不会打乱集合之间的顺序。
我们把 \(n\) 个元素按原数组顺序分成 \(b\) 个集合,每个集合有 \(k\) 个元素。
我们考虑第 \(i\) 个集合中的第 \(j\) 个元素,设其为 \(a_{i, j}\)。以下钦定 \(a_{i, j}\) 在原数组中为 \((i-1)k+j\)。首先我们询问一遍记录 \(a_{i, j}\) 的答案在集合 \(i\)。我们把 \(j\) 变成 \(b\) 进制数,从小到大枚举位,如果第 \(x\) 位为 \(y\) 那么我们在下一次询问中就把它放进集合 \((i+y) \bmod b\) 中。如此我们知道了每个元素对应的 \(i, j\),也就知道了答案。
Subtask 4
图论建模。好像这个做法虽然能启发 Subtask 6 但是和 Subtask 6 并不兼容。
因为 \(k = 2\),我们发现我们的询问和 interactive_lib 的回答都能组成一个无向图,我们问若干个边它回答若干个边。
但是因为集合之间的顺序被打乱了,我们无法从新图(回答的图)直接推出原图,因为图是自同构的。
以免你不知道自同构是什么:
正式地说,图 \(G = (V, E)\) 的自同构(Automorphism)是顶点集 \(V\) 的一个置换 \(\sigma\),它满足:顶点对 \((u, v)\) 组成一条边,当且仅当 \((\sigma(u), \sigma(v))\) 也组成一条边。即:
\[(u, v) \in E \iff (\sigma(u), \sigma(v)) \in E \]也就是说,\(\sigma\) 是从图 \(G\) 到自身的图同构。
——摘自中文维基百科
我们需要做点什么来打破图的自同构性。办法当然有很多,但是有一种方法:
我们首先询问边 \((1, 2), (3, 4), \cdots, (n-1, n)\),然后询问边 \((2, 3), (4, 5), \cdots, (n, 1)\)。我们发现边是有颜色的,即我们可以判断哪一条边出自哪一组询问,这方便了很多。
然后我们考虑给 \(1\) 增加一些特殊的边。我们询问边 \(\bold{(1, 3), (2, 4)}, (5, 6), \cdots, (n-1, n)\),然后 \(1, 2\) 就变成了特殊的,但还不够。考虑让 \(1\) 或者 \(2\) 变得更特殊一点。
我们询问边 \(\bold{(2, 4), (3, 5)}, (6, 7), \cdots, (n, 1)\)。这样只拥有来自询问 \(1, 2, 3\) 而不拥有来自询问 \(4\) 的点只有 \(1\) 一个,因此可以确定 \(1\) 的对应点。我们既然确定了 \(1\) 的对应点,就可以用任何你喜欢的方法确定其它的对应点。
Subtask 6
我们把单个集合拆成两个点,分别是 \(a_{i, 1}\) 和 \(\{a_{i, j}\}(j \in [2, k])\)。我们暂且叫它点 \(i\) 和点 \(i'\)。我们发现点 \(1\) 和点 \(1'\) 是本质不同的,这又让我们轻松了些许。虽然一次连边只能连 \(i\) 和 \(j'\) 的说。
然后我们可以连 \((1, 1'), (2, 2'), \cdots, (n, n')\),以及 \((1', 2), (2', 3), \cdots, (n', 1)\)。然后连 \((1, 2'), (1', 2), (3, 3'), \cdots, (n, n')\)。这时我们得到了那些点属于 \(i\),那些点属于 \(i'\)。但是我们需要确定 \(i\) 是什么。
在图上手摸了 5 min 以后 Hootime 发现好像不能用图的方法解决这个问题,于是 Hootime 开始想想想。
想了一会以后 Hootime 发现我们可以把 \(a_{1, 1}\) 和 \(a_{2, 1}\) 都放进集合 \(1\),然后往里填充集合 \(1\) 的元素直到塞满,最后把 \(a_{1, k}\) 放进集合 \(2\)。剩下的该去哪去哪。这样我们可以通过判断那个集合有两个 \(i\)(这里的 \(i\) 与 \(i'\) 相对)来判断集合 \(1\),然后把 \(a_{1, 1}\) 和 \(a_{2, 1}\) 抛开就可以找到某个 \(1'\) 的元素。我们结合前两次询问的数据就可以拿到 \(a_{i, 1}\)。在之后的询问中,我们每次把 \(a_{i, 1}\) 放进集合 \(i\) 就把问题弱化成了 Subtask 3。
#include <bits/stdc++.h>
#define llong long long
#define N 1005
using namespace std;
vector<vector<int>> shuffle(vector<vector<int>> boxes);
namespace Subtask4{
int deg[N], ans[N];
int to1[N][5], to2[N][5];
typedef pair<int, int> Node;
Node que[N]; int he, ta;
vector<int> solve(int n, int b, int k){
vector<vector<int>> tmp, res;
// Edges in group 1
tmp.clear(), tmp.resize(b);
for(int i = 1; i <= b; ++i)
tmp[i-1].push_back(i*2-1), tmp[i-1].push_back(i*2);
res = shuffle(tmp);
for(int i = 0; i < b; ++i){
int u1 = tmp[i][0], v1 = tmp[i][1];
int u2 = res[i][0], v2 = res[i][1];
to1[u1][1] = v1, to1[v1][1] = u1;
to2[u2][1] = v2, to2[v2][1] = u2;
}
// Edges in group 2
tmp.clear(), tmp.resize(b);
for(int i = 1; i <= b; ++i)
tmp[i-1].push_back(i*2%n+1), tmp[i-1].push_back(i*2);
res = shuffle(tmp);
for(int i = 0; i < b; ++i){
int u1 = tmp[i][0], v1 = tmp[i][1];
int u2 = res[i][0], v2 = res[i][1];
to1[u1][2] = v1, to1[v1][2] = u1;
to2[u2][2] = v2, to2[v2][2] = u2;
}
// Edges in group 3
tmp.clear(), tmp.resize(b);
for(int i = 1; i <= b; ++i)
tmp[i-1].push_back(i*2-1), tmp[i-1].push_back(i*2);
tmp[0][0] = 1, tmp[0][1] = 3;
tmp[1][0] = 2, tmp[1][1] = 4;
res = shuffle(tmp);
for(int i = 0; i < b; ++i){
int u1 = tmp[i][0], v1 = tmp[i][1];
int u2 = res[i][0], v2 = res[i][1];
to1[u1][3] = v1, to1[v1][3] = u1;
to2[u2][3] = v2, to2[v2][3] = u2;
if(v2 != to2[u2][1]) deg[u2] |= 1, deg[v2] |= 1;
}
// Edges in group 4
tmp.clear(), tmp.resize(b);
for(int i = 1; i <= b; ++i)
tmp[i-1].push_back(i*2%n+1), tmp[i-1].push_back(i*2);
tmp[0][0] = 2, tmp[0][1] = 4;
tmp[1][0] = 3, tmp[1][1] = 5;
res = shuffle(tmp);
for(int i = 0; i < b; ++i){
int u1 = tmp[i][0], v1 = tmp[i][1];
int u2 = res[i][0], v2 = res[i][1];
to1[u1][4] = v1, to1[v1][4] = u1;
to2[u2][4] = v2, to2[v2][4] = u2;
if(v2 != to2[u2][2]) deg[u2] |= 2, deg[v2] |= 2;
}
int root;
for(int i = 1; i <= n; ++i)
if(deg[i] == 1) root = i;
ans[1] = root, que[he = ta = 1] = make_pair(1, root);
while(he <= ta){
Node now = que[he++];
int u1 = now.first, u2 = now.second;
for(int i = 1; i <= 4; ++i){
int v1 = to1[u1][i], v2 = to2[u2][i];
if(ans[v1]) continue;
ans[v1] = v2, que[++ta] = make_pair(v1, v2);
}
}
vector<int> ret; ret.resize(n);
for(int i = 1; i <= n; ++i)
ret[i-1] = ans[i];
return ret;
}
}
namespace Subtask6{
#define pos(x,y) ((x-1)*k+y)
int inl[N], mch[N], nxt[N];
int a1[N], rel[N], tmpa1[N], vis[N], ans[N];
int tmpid[N];
vector<int> tmpl[N];
vector<int> solve(int n, int b, int k){
vector<vector<int>> tmp, res;
// Build edges (i, i')
tmp.clear(), tmp.resize(b);
for(int i = 1; i <= b; ++i)
for(int j = 1; j <= k; ++j)
tmp[i-1].push_back(pos(i, j));
res = shuffle(tmp);
for(int i = 0; i < b; ++i)
for(int j = 0; j < k; ++j)
inl[res[i][j]] = i, tmpl[i].push_back(res[i][j]);
// Build edges (i+1, i')
tmp.clear(), tmp.resize(b);
for(int i = 1; i <= b; ++i){
tmp[(i-2+b)%b].push_back(pos(i, 1));
for(int j = 2; j <= k; ++j)
tmp[i-1].push_back(pos(i, j));
}
res = shuffle(tmp);
for(int i = 0; i < b; ++i){
int col;
if(inl[res[i][0]] == inl[res[i][1]]) col = inl[res[i][0]];
else if(inl[res[i][0]] == inl[res[i][2]]) col = inl[res[i][0]];
else col = inl[res[i][1]];
for(int j = 0; j < k; ++j){
if(inl[res[i][j]] != col){
tmpa1[inl[res[i][j]]] = res[i][j], vis[res[i][j]] = true;
nxt[col] = res[i][j];
break;
}
}
}
// Convert
tmp.clear(), tmp.resize(b);
tmp[0].push_back(pos(1, 1)), tmp[0].push_back(pos(2, 1));
for(int i = 2; i < k; ++i)
tmp[0].push_back(pos(1, i));
tmp[1].push_back(pos(1, k));
for(int i = 2; i <= k; ++i)
tmp[1].push_back(pos(2, i));
for(int i = 3; i <= b; ++i)
for(int j = 1; j <= k; ++j)
tmp[i-1].push_back(pos(i, j));
res = shuffle(tmp);
int root;
for(int i = 0; i < b; ++i){
int cnt = 0;
for(int j = 0; j < k; ++j)
cnt += vis[res[i][j]];
if(cnt != 2) continue;
for(int j = 0; j < k; ++j){
if(vis[res[i][j]]) continue;
root = tmpa1[inl[res[i][j]]];
break;
}
break;
}
// Solve
a1[1] = root;
for(int i = 2; i <= b; ++i)
a1[i] = nxt[inl[a1[i-1]]];
for(int i = 1; i <= b; ++i)
rel[inl[a1[i]]] = i;
for(int i = 1; i <= b; ++i)
for(int j : tmpl[inl[a1[i]]])
ans[j] += (i-1)*k+1;
for(int t = 1, tt = 1; tt <= k; ++t, tt *= b){
tmp.clear(), tmp.resize(b);
for(int i = 1; i <= b; ++i){
tmp[i-1].push_back(pos(i, 1));
for(int j = 2; j <= k; ++j)
tmp[(i-1+((j-1)/tt%b))%b].push_back(pos(i, j));
}
res = shuffle(tmp);
for(int i = 1; i <= b; ++i){
for(int j : res[i-1]){
if(!vis[j]) continue;
tmpid[i] = rel[inl[j]];
break;
}
}
for(int i = 1; i <= b; ++i)
for(int j : res[i-1])
ans[j] += tt*((tmpid[i]-rel[inl[j]]+b)%b);
}
// Pack and return
vector<int> ret; ret.resize(n);
for(int i = 1; i <= n; ++i)
ret[ans[i]-1] = i;
return ret;
}
}
vector<int> solve(int n, int b, int k, int q, int ST){
if(k == 2) return Subtask4::solve(n, b, k);
else return Subtask6::solve(n, b, k);
}
#undef N

浙公网安备 33010602011771号