[lnsyoj2228]密码游戏
题意
给定序列 \(x,y\),要求构造出序列 \(a,b\),使得对于第 \(i\) 次操作,\(b_{a_{x_i}} = y\),每次操作后,将 \(a\) 循环移位一次,若 \(a\) 变为原序列,则将 \(b\) 循环移位一次
赛时 0PTS
DFS 写挂了 awa
sol
对于 \(x_i\) 和 \(y_i\),可列出
\[b_{(a_{(x_i + i) \% m} + \lfloor \frac{i}{m} \rfloor) \% m} = y_i
\]
我们取任意一对 \(y_i=y_j\),得
\[b_{(a_{(x_i + i) \% m} + \lfloor \frac{i}{m} \rfloor) \% m} = b_{(a_{(x_j + j) \% m} + \lfloor \frac{j}{m} \rfloor) \% m}
\]
由于 \(b\) 是 \(0 \sim m-1\) 的排列,因此可得
\[a_{(x_i + i) \% m} + \lfloor \frac{i}{m} \rfloor \equiv a_{(x_j + j) \% m} + \lfloor \frac{j}{m} \rfloor \pmod{m}
\]
移项,得
\[a_{(x_i + i) \% m} - a_{(x_j + j) \% m} \equiv \lfloor \frac{j}{m} \rfloor - \lfloor \frac{i}{m} \rfloor \pmod{m}
\]
这是 \(a_i - a_j \equiv k \pmod{m}\) 的形式,因此我们使用加权并查集维护。(下以 \(a_i,a_j,k\) 称呼)
可以将 \(a_i\) 和 \(a_j\) 使用并查集连接起来,使 \(a_j\) 成为 \(a_i\) 的祖宗节点。为了确保最终的根为 \(0\),从而使得 \(a_i = val_i\)(\(val_i\) 为节点 \(i\) 在并查集中的权值),我们需要使更小的节点成为另一个节点的祖宗,因此,如果 \(a_j > a_i\),需要在等式两边同时 \(\times -1\)。
在连接时,我们可将 \(a_j\) 和 \(a_i\) 的祖宗节点连接,此时,需使 \(a_j\) 到 \(a_i\) 的权值和为 \(k\),因此,这条连接的边,权值应为 \(k - val_{a_i}\),即权值和减去从 \(a_i\) 到 \(a_i\) 祖宗节点的权值和。
在枚举时,可以使用一个 vector,将 \(y\) 值相同的 \(x\) 值及其下标记录在一起。在枚举时,只需枚举同一 vector 中相邻的两元素(想一想,为什么?),并进行连边即可
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 100005, M = 30;
int n, m;
int x[N], y[N];
int f[M], d[M];
int a[N], b[N];
vector<PII> g[M];
int find(int x){
if (x == f[x]) return x;
int fa = find(f[x]);
d[x] = ((d[x] + d[f[x]]) % m + m) % m;
return f[x] = fa;
}
int main(){
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i ++ ) f[i] = i;
for (int i = 0; i < n; i ++ ) scanf("%d", &x[i]);
for (int i = 0; i < n; i ++ ) scanf("%d", &y[i]), g[y[i]].push_back({x[i], i});
for (int i = 0; i < m; i ++ )
for (int j = 1; j < g[i].size(); j ++ ){
int u = (g[i][j - 1].x + g[i][j - 1].y) % m, v = (g[i][j].x + g[i][j].y) % m, val = g[i][j].y / m - g[i][j - 1].y / m;
if (u < v) swap(u, v), val *= -1;
int fu = find(u), fv = find(v);
if (fu == fv) continue;
f[fu] = v;
d[fu] = ((val - d[u]) % m + m) % m;
}
for (int i = 0; i < m; i ++ ) find(i), a[i] = d[i];
for (int i = 0; i < n; i ++ ) b[(a[(x[i] + i % m) % m] + i / m) % m] = y[i];
for (int i = 0; i < m; i ++ ) printf("%d ", a[i]);
puts("");
for (int i = 0; i < m; i ++ ) printf("%d ", b[i]);
puts("");
}