Codeforces 1406E - Deleting Numbers(根分+數論)
一道個人感覺挺有意思的互動題,本人一開始想了個奇奇怪怪的做法,還以為卡不進去,結果發現竟然過了,而且還是正解(
首先看到這類題目可以考慮每次刪掉一個質數的倍數,即對某個 \(pr_i\) 執行 B 操作,然後對 \(pr_i\) 進行 A 詢問,根據詢問得到的值是否等於理論上刪去這些數後 \(pr_i\) 的倍數個數來判斷 \(pr_i\) 是否是 \(x\) 的因數,如果是那麼就列舉 \(pr_i\) 的 \(2,3,4,\cdots\) 次方,再對這些次方執行 A 詢問,以此來判斷 \(x\) 質因數分解式中 \(pr_i\) 的次數。
算下詢問次數,詢問 \(pr_i\) 的 \(2,3,4,\cdots\) 的次數顯然與 \(x\) 質因數分解式中 \(x\) 每個質因數的次數之和有關,是 \(\log\) 級別的,最多隻有 \(20\) 可以忽略,瓶頸在於每個質因數都要進行兩次操作,打表可以發現 \(10^5\) 以內質因數個數剛好比 \(10^4\) 小個幾百,因此 \(2\pi(n)\) 是 \(2\times 10^4\) 級別的,過不去。
考慮優化,很明顯對於 B 操作而言,我們是可以在操作的同時問出 \(x\) 的倍數的,而剛剛的解法中並沒有利用這個性質,因此考慮從這個性質入手解題,我們還是從小到大列舉質因子,只不過與剛才不同的一點是,我們不每進行一次 B 詢問都跟一個 A 詢問,我們只做 B 詢問,那麼對於大多數質數,每次 B 詢問時也可以根據結果知道該質數是否是 \(x\)
注意到 A 操作起始下標是可以從 \(1\) 開始的,而通過 \(A\ 1\) 我們則可以知道剛才詢問的數中是否出現了 \(x\) 的質因子,因此我們考慮根分,每 100 次 B 詢問來一次 A 1,那麼我們可以把質因子按大小分為 \(\lceil\dfrac{\pi(n)}{100}\rceil\),顯然最小質因子就在最後一個 A 1 等於剩餘數個數與第一個 A 1 不等於剩餘數個數的位置之間,中間部分就暴力列舉質因子然後用 A 質因子判斷即可,這樣總操作次數為 \(\pi(n)+100+\lceil\dfrac{\pi(n)}{100}\rceil+\log n\)
const int MAXN=1e5;
int n,pr[MAXN/5+5],prcnt=0;
bool vis[MAXN+5],is[MAXN+5],has[MAXN+5];
vector<int> fac[MAXN+5];
int cnt[MAXN+5];
void sieve(){
for(int i=2;i<=n;i++){
if(!vis[i]) pr[++prcnt]=i;
for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
vis[pr[j]*i]=1;
if(i%pr[j]==0) break;
}
}
for(int i=1;i<=n;i++) cnt[i]=n/i;
for(int i=1;i<=prcnt;i++) for(ll j=pr[i];j<=n;j*=pr[i]) is[j]=1;
for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i) fac[j].pb(i);
}
void del(int x){for(int y:fac[x]) cnt[y]--;has[x]=1;}
void del_mul(int x){for(int i=x;i<=n;i+=x) if(!has[i]) del(i);}
int qr1(int x){printf("A %d\n",x);fflush(stdout);int res;scanf("%d",&res);return res;}
int qr2(int x){printf("B %d\n",x);fflush(stdout);int res;scanf("%d",&res);return res;}
int main(){
scanf("%d",&n);sieve();
int cur=1,mn=prcnt,cc=0,fst_blk=0;
for(int i=1;i<=prcnt;i++){
int t=qr2(pr[i]);
if(t!=cnt[pr[i]]){
if(cur==1) mn=i;del_mul(pr[i]);
cur*=pr[i];
for(ll x=1ll*pr[i]*pr[i];x<=n;x*=pr[i]){
int num=qr1(x);//printf("%d %d\n",x,cnt[x]);
if(num==cnt[x]) break;cur*=pr[i];
}
} else del_mul(pr[i]);
++cc;
if(cc==100&&!fst_blk){
cc=0;int x=qr1(1);
if(x!=cnt[1]) fst_blk=i;
}
} if(!fst_blk){int x=qr1(1);if(x!=cnt[1]) fst_blk=prcnt;}
for(int j=(fst_blk-1)/100*100+1;j<=min(fst_blk,mn);j++){
int x=qr1(pr[j]);
if(x!=cnt[pr[j]]){
cur*=pr[j];
for(ll k=1ll*pr[j]*pr[j];k<=n;k*=pr[j]){
int num=qr1(k);//printf("%d %d\n",x,cnt[x]);
if(num==cnt[k]) break;cur*=pr[j];
} break;
}
}
printf("C %d\n",cur);fflush(stdout);
return 0;
}