1. 程式人生 > 其它 >Codeforces 1406E - Deleting Numbers(根分+數論)

Codeforces 1406E - Deleting Numbers(根分+數論)

根分+數論,hot tea

Codeforces 題面傳送門 & 洛谷題面傳送門

一道個人感覺挺有意思的互動題,本人一開始想了個奇奇怪怪的做法,還以為卡不進去,結果發現竟然過了,而且還是正解(

首先看到這類題目可以考慮每次刪掉一個質數的倍數,即對某個 \(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\)

的因子,只有一個例外——那就是 \(x\) 的最小質因子,因此接下來我們只用找到 \(x\) 的最小質因子及它的次數即可,這也是本題的難點所在,我就在這個地方卡了 20+min 後想到了這個解法。

注意到 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\)

,實測詢問次數最多 9700+ 次,剛好卡過。

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;
}