Codeforces 1406E 时间复杂度优化 + 思维

       给你一个 n n n,要求你找到一个 1 − n 1-n 1n 以内的 x x x。给定三种操作, A   a A\ a A a 查询当前有多少 a a a 的倍数, a a a 范围 1 − n 1-n 1n B   a B\ a B a 查询当前有多少 a a a 的倍数,并删掉所有除 x x x a a a 的倍数, a a a 范围 2 − n 2-n 2n C a C a Ca,表示打印答案。

        1 e 5 1e5 1e5 以内的质数有 9592 9592 9592 个,这意味着最多只能查询所有质数一遍。我们考虑求出 a n s ans ans 的质因子,我们先算出筛掉该质数 a a a 时,应该会删掉多少数,然后 A   1 A\ 1 A 1 去判断真实情况还留下多少数。这里要注意的是, A   1 A\ 1 A 1 只有第一次使用的时候有效,因为当你发现 a n s ans ans 存在某个质因子的时候,你再也无法得知后面的数是不是它的质因子,因为 a n s ans ans 在之前就本来就该被筛掉的。

       接下来就是精髓操作,如何只遍历所有质数一遍?我们考虑查询 p r i m e ∗ a n s prime*ans primeans 的结果,因为 a n s ans ans 中的质因子本来早该被删,但 B B B 操作保留了 x x x,于是我们就可以借此判断 x x x 中还是否包含其他质因子。至于质因子的幂次,二分即可。这里要简单的限制一下上限,如果你 a n s ans ans 乘上一个质因子会比 n n n 大,那显然错误,所以也不用再找。

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long LL;
const int maxn = 1e5 + 5;
int cnt;
int prime[maxn];
bool judge[maxn];
int sum[maxn], sqn[maxn];
inline void init(int n){
    for(int i = 2; i <= n; i++){
        if(!judge[i]){
            prime[++cnt] = i;
            for(int tol = i; tol <= n; tol += i){
                if(!judge[tol]){
                    sum[i]++;
                    judge[tol] = true;
                }
            }
        }
    }
    for(int i = 2; i <= n; i++)
        sum[i] += sum[i - 1];
}

inline int num(int p, int c){
    int ans = 1;
    while(c--){
        ans *= p;
    }
    return ans;
}

inline int cal(int n, int p){
    int l = 1, r = 1, x;
    while(n >= p){
        n /= p;
        r++;
    }
    if(r == 2)  return p;
    while(l < r){
        int mid = (l + r)/2;
        if(mid > 1){
            cout << "A " << num(p, mid) << endl;
            cout.flush();
            cin >> x;
        }else x = 1;
        if(x)
            l = mid + 1;
        else
            r = mid;
    }
    return num(p, l - 1);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, x, ans = 1, k, sq, tol, c, flag;
    cin >> n;
    tol = n;
    init(n);
    sq = sqrt(cnt);
    flag = cnt + 1;
    for(k = 0; sqn[k] < cnt;)
        sqn[++k] = k*sq;
    sqn[k] = cnt;
    for(int i = 0; i < k; i++){
        for(int j = sqn[i] + 1; j <= sqn[i + 1]; j++){
            cout << "B " << prime[j] << endl;
            cout.flush();
            cin >> x;
        }
        tol -= sum[prime[sqn[i + 1]]] - sum[prime[sqn[i]]];
        cout << "A 1" << endl;
        cout.flush();
        cin >> x;
        if(x == tol) continue;
        else{
            for(int j = sqn[i] + 1; j <= sqn[i + 1] && n >= prime[j]; j++){
                cout << "A " << prime[j] << endl;
                cout.flush();
                cin >> x;
                if(x){
                    c = cal(n, prime[j]);
                    ans *= c;
                    n /= c;
                }
                flag = j + 1;
            }
            break;
        }
    }
    for(int i = flag; n >= prime[i] && i <= cnt; i++){
        cout << "A " << prime[i]*ans << endl;
        cout.flush();
        cin >> x;
        if(x){
            c = cal(n, prime[i]);
            ans *= c;
            n /= c;
        }
    }
    cout << "C " << ans << endl;
    cout.flush();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值