CF995E Number Clicker

发布时间 2023-12-16 20:24:08作者: Hanx16Msgr

Number Clicker

Luogu CF995E

题面翻译

小 y 在玩数学游戏,他有三种变化方式:

  1. 将该数 \(+1\)
  2. 将该数 \(-1\)
  3. 将该数变成他的逆元(即 \(p-2\) 次幂),当然,我们所有操作都是在 \(\bmod\ p\) 意义下的

现在小 h 知道了变换前的数 \(u\) 和变换后的数 \(v\),给定模数 \(p\) 保证是质数,他想知道怎么在 \(200\) 次操作之内得到 \(v\)

\(p\le 10^9+9\)

Solution

尝试去找了一下三种操作之间有什么规律,然后发现确实没什么规律……

考虑构造一种操作,使它与题目要求的变化方式等价。由于存在逆元,所以我们将这个数表示成为 \(\dfrac a b\)。那么操作 \(1\) 对应 \(\dfrac {a+b}{b}\),操作 \(2\) 对应 \(\dfrac {a-b}{b}\),操作 \(3\) 对应 \(\dfrac b a\)。发现这个操作很像辗转相除法,所以考虑使用更相减损法来做这道题。

因为操作 \(1\) 和操作 \(2\) 是可逆的,并且操作 \(3\) 和操作 \(3\) 也是可逆的,所以可以考虑将变换前的数 \(u\) 和变换后的数 \(v\) 都变换成为一个数字后然后将一个操作序列反过来。不妨将这个数字设成 \(0\)。那么就是运用更相减损法将 \(u\)\(v\) 分别变成 \(0\)

初始的 \(a,b\) 并不好确认,所以可以直接随机 \(a\) 的取值,然后计算 \(b\)。如果直接使用更相减损法可能会直接超时,所以可以先使用辗转相除法计算步数,如果总步数 \(\le 200\) 就使用更相减损法构造答案。

复杂度 \(\mathcal O(\text{松松松})\)

Code
// Cirno is not baka!
#include <bits/stdc++.h>
#define For(i, a, b) for (int i = (a); i <= (int)(b); ++i)
#define Rof(i, a, b) for (int i = (a); i >= (int)(b); --i)
#define All(x) x.begin(), x.end()
#define pii pair<int, int>
#define fi first
#define se second
#define i64 long long
#define mkp make_pair
// #define int long long
#define epb emplace_back
using namespace std;

const int _N = 1e6 + 5, inf = 1e9;
template<typename T> void Max(T &x, T y) {x = max(x, y);}
template<typename T> void Min(T &x, T y) {x = min(x, y);}
#ifdef CIRNO
template<typename T> void Debug(T x) {cerr << x << '\n';}
template<typename T, typename ...Args> void Debug(T x, Args ...args) {cerr << x << ' '; Debug(args...);}
template<typename T> void Debug(vector<T> v) {for (T x: v) cerr << x << ' '; cerr << '\n';}
#else
#define Debug(...)
#endif

namespace BakaCirno {
    mt19937 rnd(9);
    #define Rand(l, r) uniform_int_distribution<>(l, r)(rnd)
    int A, B, mod;
    int Calc(int a, int b) {
        if (b == 0) return 0;
        return Calc(b, a % b) + a / b + 1;
    }
    vector<int> GetPath(int a, int b) {
        vector<int> res;
        while (b) {
            if (a < b) res.epb(3), swap(a, b);
            else res.epb(2), a -= b;
        }
        return res;
    }
    void _() {
        cin >> A >> B >> mod;
        while (true) {
            int v1 = Rand(1, mod - 1), v2 = Rand(1, mod - 1);
            if (Calc(1ll * v1 * A % mod, v1) + Calc(1ll * v2 * B % mod, v2) <= 200) {
                vector<int> r1 = GetPath(1ll * v1 * A % mod, v1),
                            r2 = GetPath(1ll * v2 * B % mod, v2);
                reverse(All(r2));
                cout << r1.size() + r2.size() << '\n';
                for (int x: r1) cout << x << ' ';
                for (int x: r2) cout << (x < 3 ? 3 - x : 3) << ' ';
                cout << '\n';
                break;
            }
        }
    }
}

void File(const string file) {
    freopen((file + ".in").c_str(), "r", stdin);
    freopen((file + ".out").c_str(), "w", stdout);
}
signed main() {
    // File("");
    cin.tie(0)->sync_with_stdio(0); int T = 1;
    // cin >> T;
    while (T--) BakaCirno::_();
}