嘘~ 正在从服务器偷取页面 . . .

CF995E Number Clicker 题解


CF995E Number Clicker 题解

题目链接:Number Clicker

题意

有三种操作:

  1. 令 $u \gets u + 1 \pmod p$
  2. 令 $u \gets u - 1 \pmod{p}$
  3. 令 $u \gets u^{p-2} \pmod{p}$

给定 $u,v$ 和质数 $p$ ,构造一种方案让 $u$ 变成 $v$ ,操作数不超过 $200$ 次。

输入格式

一行, $u,v,p$ 。

输出格式

第一行输出操作次数,第二行输出每次操作的序号 $c_i(1\le c_i \le 3)$ 。

数据范围

$0 \le u,v \le p - 1,~3 \le p \le 10^9 + 9$ 。

直接 bfs 的话肯定是过不了的,不过因为这里的操作都是可逆的,所以我们可以尝试双向 bfs 。

因为 $u^{p-2} \bmod{p}$ 的值难以预测,所以 $u \gets u^{p-2} \pmod{p}$ 这个操作可以近似看作随机的。

由于我们的算法可以看作在 $[0,p-1]$ 随机了 $n\le 2k$ 个点,

那么根据生日悖论,至少两个点相同的概率 $p(n)$ ,可以用以下公式逼近。

用 wolfram 跑了一下 ,$n$ 大约在 $10^5$ 的时候 $p(n) > 0.99$ ,假装它是 $\mathcal{O}(\sqrt{p})$ 的吧。

因此时间复杂度大约是 $\mathcal{O}(\sqrt{p} \log p)$

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
typedef long long ll;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define N ((int)())

int P, top, stk[233];
queue<int> q1, q2; unordered_map<int, int> vis1, vis2, nxt, pre;
int qpow(int a, int b)
{
    int r = 1;
    for(a %= P; b; b >>= 1, a = (ll)a * a % P)
        if(b & 1) r = (ll)r * a % P;
    return r;
}
void output(int u)
{
    cout << vis1[u] + vis2[u] - 2 << '\n';
    int x = u; top = 0;
    while(x != -1) { stk[++top] = x, x = pre[x]; }
    for(; top > 1; --top)
    {
        const int a = stk[top]; 
        const int b = stk[top - 1];
        if((a + 1) % P == b) cout << "1 ";
        else if((a + P - 1) % P == b) cout << "2 "; else cout << "3 ";
    }
    for(x = u; nxt[x] != -1; x = nxt[x])
    {
        if((x + 1) % P == nxt[x]) cout << "1 ";
        else if((x + P - 1) % P == nxt[x]) cout << "2 "; else cout << "3 ";
    }
}
void work()
{
    static int u, a, b, c;
    u = q1.front(); q1.pop();
    if(vis2[u]) { output(u); exit(0); }
    a = (u + 1) % P, b = (u + P - 1) % P, c = qpow(u, P - 2);
    if(!vis1[a]) { q1.push(a); vis1[a] = vis1[u] + 1; pre[a] = u; }
    if(!vis1[b]) { q1.push(b); vis1[b] = vis1[u] + 1; pre[b] = u; }
    if(!vis1[c]) { q1.push(c); vis1[c] = vis1[u] + 1; pre[c] = u; }
    u = q2.front(); q2.pop();
    a = (u + 1) % P, b = (u + P - 1) % P, c = qpow(u, P - 2);
    if(!vis2[a]) { q2.push(a); vis2[a] = vis2[u] + 1; nxt[a] = u; }
    if(!vis2[b]) { q2.push(b); vis2[b] = vis2[u] + 1; nxt[b] = u; }
    if(!vis2[c]) { q2.push(c); vis2[c] = vis2[u] + 1; nxt[c] = u; }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int u, v; cin >> u >> v >> P;
    q1.push(u); vis1[u] = 1; pre[u] = -1;
    q2.push(v); vis2[v] = 1; nxt[v] = -1;
    while(!q1.empty()) work();
    return 0;
}

参考文献

有关这题的随机性怎么证明。

官方题解说这是基于一些关于扩展图 (expander graphs) 的已知数论结果。

顺便说关键词是 Margulis 扩展图。不过我也没找到这个东西,不懂。

[1] https://www.luogu.com.cn/article/5wkpemws


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录