new Date()日期格式處理
本來是本著口胡一下的原則去想的,但是最後又調了一個晚上
首先我們有一個很naive的想法,可以列舉每一個質數\(p\),先刪去它的倍數然後在問是否有\(p\)的倍數,就可以用兩次操作判斷出\(p\)是否為\(x\)的倍數了
然後我們粗略一算,\(10^5\)以內的質數有九千多個,直接GG了
但是我們發現這個想法很好,因為涉及到質數的情況很容易按\(\sqrt n\)分開來討論
因此我們考慮先把\(\sqrt n\)內的\(x\)的約數序列\(d_{1\cdots m}\)求出來,然後從\(cur=\prod_{i=1}^m d_i\)開始擴充套件答案
每次嘗試不斷地給\(cur\)乘上\(d_i\)
由於每個\(d_i\)更新\(cur\)最多隻有$\log $級別的次數,因此可以保證操作次數
但是我們發現此時\(x\)可能還有一個約數是大於\(\sqrt n\)的質數,容易發現當我們刪除完所有的小質數的倍數後,剩下的數恰好是所有的大質數(或者包含\(x\))
因此我們可以去列舉所有大質數\(p\),判斷\(A(p)\)是否等於\(2\)即可(即剩下了\(p\)和\(p\times cur\)),答案就是\(p\times cur\)
所以接下來我們就要討論答案就是一個大質數的情況了,由於大質數很多,如果我們還是對於每個數都花上\(2\)次操作去詢問那麼顯然還是GG了
於是陳指導提出了奇妙的閾值做法,具體地,我們設\(S\)表示刪去連續的\(S\)個數之後來檢驗一次答案是否在這\(S\)個數之中
若在的話我們只需要在這\(S\)個數之件掃一遍進行詢問就可以得出答案了
所以我們要最小化\(S+\frac{num}{S}\)(\(num\)表示剩下的大質數數量),取\(S=\sqrt {num}\)即可,此時詢問次數\(num+2\sqrt {num}\)
實測可以完美的卡著上限通過此題
#include<cstdio> #include<iostream> #include<cmath> #define RI register int #define CI const int& #define A(x) (printf("A %d\n",x),fflush(stdout),scanf("%d",&res),res) #define B(x) (printf("B %d\n",x),fflush(stdout),scanf("%d",&res),res) #define C(x) (printf("C %d\n",x),fflush(stdout)) using namespace std; const int N=100005; int n,p[N],t[N],tot,res,cnt,cur,ct,S; bool vis[N]; inline void init(CI n) { RI i,j; for (i=2;i<=n;++i) { if (!vis[i]) p[++cnt]=i; for (j=1;j<=cnt&&i*p[j]<=n;++j) { vis[i*p[j]]=1; if (i%p[j]==0) break; } } } inline int find(CI l,CI r) { for (RI i=l;i<=r;++i) if (A(p[i])) return p[i]; } int main() { RI i; for (scanf("%d",&n),init(n),i=1;i<=cnt&&p[i]*p[i]<=n;++i) if (B(p[i]),A(p[i])) t[++tot]=p[i]; int tp=i; if (tot) { for (cur=i=1;i<=tot;++i) cur*=t[i]; for (i=1;i<=tot;++i) while (cur*t[i]<=n&&A(cur*t[i])) cur*=t[i]; for (i=tp;i<=cnt;++i) if (A(p[i])==2) return C(cur*p[i]),0; return C(cur),0; } for (cur=cnt-i+2,S=sqrt(cur);i<=cnt;++i) if (B(p[i]),--cur,++ct==S) { if (A(1)!=cur) return C(find(i-S+1,i)),0; ct=0; } return C(A(1)!=1?find(cnt-ct+1,cnt):1),0; }