[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("");
}
posted @ 2024-08-01 19:58  是一只小蒟蒻呀  阅读(8)  评论(0编辑  收藏  举报