Codeforces Round #670 (Div. 2) E. Deleting Numbers

题目链接

一、题意

有一个未知数x,满足1\leqslant x \leqslant n

现在有一个集合\left \{ 1,2,3,...,n \right \}

你知道n,想通过总数不超过10000次以下操作知道x

有如下三种操作:

A\;a(a\geqslant 1):询问a的倍数有几个。

B\;a(a\geqslant 2):询问a的倍数有几个。然后把a的所有倍数从集合中删除,注意x不会从集合中删除。

C\;a:输出答案,x=a

数据范围:1 \leqslant n \leqslant 10^5

二、题解

首先打个表发现不超过n的素数有9592个,很接近询问的上限10000

考虑分块。

设不超过n的素数有m个,每块有\sqrt{m}个素数。

每次用第2种操作询问\sqrt{m}个素数,也就是删除操作。维护res,表示认为x可以删除,集合中剩余的数。

在对一整块数进行删除操作后,用第一种操作A \; 1询问,用num表示1的倍数的剩余个数,也就是集合中剩余数的个数。

如果该块素数都不是x的因子,那么num=res

重复操作,直到在一块数在进行删除操作后出现num \neq res

我们已经知道x的最小因子在哪一块中,并且res认为x已经被删除。

那么我们可以对之后的每一个素数进行第一种操作A \; p , A \; p^2 , A \; p^3 , ...p是枚举的素数。

在知道p^{k+1} \nmid x时可以停止对p的询问。

我们可以知道p^k,满足p^k \mid x,p^{k+1} \nmid x

三、代码

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
vector<int> p ;
struct Easymath
{
    ll qpow(ll a , ll b) //快速幂
    {
        if(b < 0)  return 0 ;
        ll ans = 1 ; 
        a %= mod ;
        while(b)
        {
            if(b & 1)  ans = (ans * a) % mod ;
            b >>= 1 , a = (a * a) % mod ;
        }
        return ans % mod ;
    }
    ll ksc_log(ll x , ll y , ll mod) //快速乘
    {
        x %= mod , y %= mod ;
        ll ans = 0;
        while(y)
        {
            if(y & 1) ans = (ans + x) % mod ; 
            y >>= 1 ;
            x = (x + x) % mod ;
        }
        return ans;
    }
    ll ksc_O1(ll x , ll y , ll mod) //快速乘
    {
        x %= mod , y %= mod ;
        ll z = (ld)x * y / mod ;
        ll ans = x * y - z * mod ;
        if(ans < 0)  ans += mod ;
        else if(ans >= mod)  ans -= mod ;
        return ans ;
    }
    int cnt = 0 ;
    bool vis[maxn] ;
    int prime[maxn] ;
    void get_prime(int up) //素数筛
    {
        memset(vis , 0 , sizeof(vis)) ;
        vis[1] = 1 ;
        for(int i = 2 ; i <= up ; i ++)
        {
            if(!vis[i])  prime[++ cnt] = i , p.pb(i) ;
            for(int j = 1 ; j <= cnt && i * prime[j] <= up ; j ++)
            {
                vis[i * prime[j]] = 1 ;
                if(i % prime[j] == 0) break ;
            }
        }
    }
    //begin 判定大素数
    ll mul(ll a , ll b , ll mod)
    {
        ll ret = 0 ;
        while(b) 
        {
            if(b & 1)  ret = (ret + a) % mod ;
            a = (a + a) % mod ;
            b >>= 1 ;
        }
        return ret ;
    }
    ll pow(ll a , ll b , ll mod)
    {
        ll ret = 1 ;
        while(b) 
        {
            if(b & 1)  ret = mul(ret , a , mod) ;
            a = mul(a , a , mod) ;
            b >>= 1 ;
        }
        return ret ;
    }
    bool check(ll a , ll n)
    {
        ll x = n - 1 ;
        int t = 0 ;
        while((x & 1) == 0) 
        {
            x >>= 1 ;
            t ++ ;
        }
        x = pow(a , x , n) ;
        ll y ;
        rep(i , 1 , t)
        {
            y = mul(x , x , n) ;
            if(y == 1 && x != 1 && x != n - 1)  return 1 ;
            x = y ;
        }
        if(y != 1) return 1 ;
        return 0 ;
    }
    bool Miller_Rabin(ll n) 
    {
        if(n == 2)  return 1 ;
        if(n == 1 || !(n & 1))  return 0 ;
        const int arr[12] = {2,3,5,7,11,13,17,19,23,29,31,37} ;
        rep(i , 0 , 11) 
        {
            if(arr[i] >= n) break ;
            if(check(arr[i] , n)) return 0 ;
        }
        return 1 ;
    }
    //end 判定大素数
    ll get_inv(ll x) //逆元
    {
       return qpow(x , mod - 2) % mod ;
    }
    ll inv1[maxn] ;  //乘法逆元
    void init1(int up)
    {
       inv1[1] = 1 ;
       for(int i = 2 ; i <= up ; i ++)
         inv1[i] = (ll)(mod - mod / i) * inv1[int(mod % (ll)i)] % mod ;
    }
    ll fac[maxn] ;
    ll inv[maxn] ; //阶乘逆元
    void init(int up) 
    {
       fac[0] = fac[1] = inv[0] = inv[1] = 1 ;
       for(int i = 2 ; i <= up ; i ++)
       {
         fac[i] = fac[i - 1] * i % mod ;
         inv[i] = -inv[mod % i] * (mod / i) % mod ;
         while(inv[i] < 0) inv[i] += mod ;
       }
       for(int i = 2 ; i <= up ; i ++)  
         inv[i] = inv[i] * inv[i - 1] % mod ;
    }
    ll C(int n , int m)
    {
        return fac[n] * inv[m] % mod * inv[n - m] % mod ;
    }
} em ;
bool used[maxn] ;
int ask(int i)
{
    printf("A %d\n" , i) ;
    fflush(stdout) ;
    int x ;
    scanf("%d" , &x) ;
    return x ;
}
int del(int i)
{
    printf("B %d\n" , i) ;
    fflush(stdout) ;
    int x ;
    scanf("%d" , &x) ;
    return x ;
}
int main()
{
    int n ;
    scanf("%d" , &n) ;
    em.get_prime(n) ;
    int cnt = em.cnt ;
    int blk = sqrt(cnt) ;
    int ans = 1 ;
    int res = n ;
    int siz = cnt - 1 ;
    int tmp = n + 1 ;
    rep(i , 0 , siz)
    {
        int nxt = i + blk - 1 ;
        nxt = min(nxt , siz) ;
        rep(j , i , nxt)
        {
            int x = p[j] ;
            del(x) ;
            for(int k = x ; k <= n ; k += x)  if(!used[k])  used[k] = 1 , res -- ;
        }
        int num = ask(1) ;
        if(num != res)
        {
            tmp = i ;
            break ;
        }
        i = nxt ;
    }
    rep(i , tmp , siz)
    {
        int x = p[i] ;
        int res = 0 ;
        if(ans * x > n)  break ;
        for(ll k = x ; k <= n ; k *= x)  if(!used[k])  res ++ ;
        for(ll k = x ; k <= n ; k *= x)
        {
            int num = ask(k) ;
            if(num != res)  ans *= x ;
            else break ;
            if(!used[k])  res -- ;
        }
    }
    printf("C %d\n" , ans) ;
    fflush(stdout) ;
    return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值