1 条题解
-
6
说句闲话,这个 idea 是我在去年暑假的一天里梦见的。看到我们只能询问离散对数方程的解,不难想到原根。因为原根有一个很好的性质:在模 意义下,若 为 的原根, 是一定有解的。
但题目没有给我们 的原根,考虑猜出原根 。如果直接从 开始枚举 并判断,最坏情况下最小的 是 的,无法通过所有数据。但注意到对于质数 ,其原根个数为 ,这个数字大概在 之间,也就是说,我们期望 次左右就能随机出一个 的原根。
现在考虑判断一个数 是否为 的原根。首先我们通过 判掉 为 的倍数的情况。尽管我们不能知道 是什么,但注意到上面提到的原根的性质,我们可以随机若干正整数 ,仍然先判掉 为 的倍数的情况,然后看 是否有解,如果无解,则 不为 的原根。考虑到所有非 的原根的 都存在一个 ,使 且 ,当 无解即 时可以判定 不为 的原根。也就是说,假如无解,我们期望 次左右随机出一个足以判定 不满足条件的 。为了保险,这里我们可以取 次。
求完原根后,我们来构造方程。由于题目允许我们对非常大的 做出询问(不超过 量级),考虑随机一个 ,令 ,,。由于 为质数,根据欧拉定理,我们有 ,进而可以得到 。
于是我们把询问次数卡满,对每次得到的 求 ,将最终所得的值 即可得到 。这样看上去很不靠谱,但实际上极大概率可以得到一个合法的解,因为我们可以将 视为一个随机数且存在下面这个结论:
- 两个 级别的随机数的 的期望大小是 的。
证明:$O(\dfrac{\displaystyle\sum_{i = 1}^n \sum_{j = 1}^n \gcd(i, j)}{n^2}) = O(\dfrac{\displaystyle\sum_{d = 1}^n \varphi(d) \lfloor \frac{n}{d} \rfloor^2}{n^2}) = O(\displaystyle\sum_{d = 1}^n \frac{\varphi(d)}{d^2}) \leq O(\displaystyle\sum_{d = 1}^n \frac{1}{d}) = O(\ln n)$。
于是我们只需要询问期望 ()次即期望 次左右就可以得到 的值。当然因为两边值域不一定对称,实测在保证可以得到答案的前提下需要 次。
最终我们只需要期望 次左右的询问即可得到答案,实测在保证可以得到答案的前提下一般需要 次左右,最大需要 次左右。
代码:
#include <iostream> #include <vector> #include <iostream> #include <vector> #include <random> #include <ctime> using namespace std; typedef long long ll; const int N = 50 + 7; const ll M = 1e18; int t, cnt = 0; ll bucket[N]; mt19937 e(time(NULL)); uniform_int_distribution<ll> rnd(1, M); inline ll ask(int n, int m, ll u, ll v, vector<ll> c, vector<ll> d, vector<ll> e, vector<ll> f){ ll ans = 0; cout << "ask " << n << " " << m << " " << u << " " << v << "\n"; for (register int i = 0; i < n; i++){ cout << c[i] << " "; } cout << "\n"; for (register int i = 0; i < m; i++){ cout << d[i] << " "; } cout << "\n"; for (register int i = 0; i < n; i++){ cout << e[i] << " "; } cout << "\n"; for (register int i = 0; i < m; i++){ cout << f[i] << " "; } cout << endl; cin >> ans; return ans; } inline ll make(){ ll ans; do { ans = rnd(e); t--; } while (ask(1, 1, ans, 0, vector<ll>{1}, vector<ll>{0}, vector<ll>{1}, vector<ll>{0}) != -1); return bucket[++cnt] = ans; } ll gcd(ll a, ll b){ return b == 0 ? a : gcd(b, a % b); } inline void answer(ll p_){ cout << "answer " << p_ << endl; } int main(){ ll g, phi_p = 0; cin >> t; while (true){ int pos = 1; bool flag = true; g = make(); for (register int i = 1; i <= 10; i++){ if (pos > cnt){ make(); } else if (bucket[pos] == g){ pos++; if (pos > cnt) make(); } t--; if (ask(1, 1, g, bucket[pos], vector<ll>{1}, vector<ll>{1}, vector<ll>{1}, vector<ll>{1}) == -1){ flag = false; break; } pos++; } if (flag) break; } while (--t >= 0){ ll x0 = rnd(e), x = ask(1, 1, g, g, vector<ll>{1}, vector<ll>{x0}, vector<ll>{1}, vector<ll>{1}); phi_p = gcd(phi_p, x0 - x); } answer(phi_p + 1); return 0; }
- 1
信息
- ID
- 278
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- 递交数
- 35
- 已通过
- 14
- 上传者