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

CF1355F Guess Divisors Count 题解


CF1355F Guess Divisors Count 题解

题目链接:Guess Divisors Count

题意

要求猜一个 \(1\)\(10^9\) 之间的一个数 \(X\)

你可以询问一个数 \(Q\)\(1\le Q\le10^{18}\)),然后得到 \(\gcd(X,Q)\)

请你在不多于 \(22\) 次询问后算出 \(X\) 的因数个数。

注意:设你的答案为 \(d\),标准答案为 \(r\),只要满足 \(|r-d|\le7\)\(\frac12\le\frac{r}{d}\le2\) 即算正确。

输入格式

首先输入一个正整数,数据组数 \(t(1\le t \le 100)\)

接下来 \(t\) 组数据。

交互格式

  • 询问:? Q
  • 回答:! d。(注意每次输出后要刷新缓冲区)。

本题特色:乱搞?

感觉很多这种类型的题就和快慢刀一样,主打一个迷惑人,其实本质没有那么难。

首先,根据唯一分解定理可知若 \(X = \prod_{i=1}^n p_i^{x_i}\) ,则因数个数为 \(\prod_{i=1}^n\left(x_i+1\right)\)

因此不考虑询问个数,大量询问各个质数和质数的幂即可猜出答案。

但是可以预见的是,很多时候得到的答案都是 \(1\) ,本题要求我们优化这个询问的思路。

那么,如果我们一口气询问多个质数的乘积,是否更可能获得答案呢?

于是我们试图维护一个由质数组成的集合,每次取出若干元素取出并相乘(要求乘积不超过 \(10^{18}\)​ )

在询问后我们可以立即得到哪些质数有用,哪些不再有用,并将有用的那些质数的次数加 \(1\) 再塞进去。

于是我们需要将所有的质数按次幂降序为第一关键字,质数大小为第二关键字排序,这可以用优先队列维护。

考虑仅执行 \(22\) 次询问,根据实验可发现通常至多询问到 \(900\) 左右。

此时我们其实已经得到了一个模糊的答案,下面证明这个答案恰好满足题目的奇怪条件。

显然 \(850\) 以下的所有质因子我们都已经搞定了,对于 \(X\) 的其余情况:

  • \(1\) ,没有问题
  • 质数,答案需要乘以 \(2\) (乘法原理)
  • 质数的平方,答案需要乘以 \(3\)
  • 质数乘质数,答案需要乘以 \(4\)
  • 三个质数相乘。因为三个质因子都大于 \(850\) ,所以实际上不会有其他质因子了,此时的答案为 \(8\)

有意思的是,如果我们把输出的答案乘以 \(2\) ,上面的所有情况都满足了题目的限制,真是神奇呐

时间复杂度 \(\mathcal{O}(\mathrm{ok})\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
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)(40000 + 15))

const int _ = 40000;
char ck[N]; int pcnt, p[N];
struct node { int x, y, z; } b[N];
bool operator<(node a, node b) { return a.y == b.y ? a.x > b.x : a.y < b.y; }
priority_queue<node> q;
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
void solve()
{
    int res = 1; while(!q.empty()) q.pop();
    for(int i = 1; i <= pcnt; i++) q.push({p[i], 1, p[i]});
    for(int t = 1; t <= 22; t++)
    {
        int x = 1, cnt = 0;
        while(!q.empty()) {
            auto tmp = q.top(); q.pop();
            if(1.0 * tmp.x * x > 1e18) { q.push(tmp); break; }
            b[++cnt] = tmp; x *= tmp.x;
        }
        cout << "? " << x << '\n'; cout.flush();
        int g; cin >> g;
        for(int i = 1; i <= cnt; i++)
            if(g % b[i].x == 0) {
                res /= b[i].y; ++b[i].y; res *= b[i].y;
                b[i].x *= b[i].z; q.push(b[i]);
            }
    }
    cout << "! " << res * 2 << '\n'; cout.flush();
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    for(int i = 2; i <= _; i++)
    {
        if(!ck[i]) p[++pcnt] = i;
        for(int j = 1; j <= pcnt && i * p[j] <= _; j++) {
            ck[i * p[j]] = true; if(i % p[j] == 0) break;
        }
    }
    int qwq; cin >> qwq; while(qwq--) solve();
    return 0;
}

参考文献

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


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