Nginx配置實現下載檔案的示例程式碼
題目來源:Codeforces, CF1406E Deleting Numbers
題目大意
互動題。
有一個未知的整數 \(x\) (\(1\leq x\leq n\)),你需要通過互動找出它。
初始時,互動庫會告訴你 \(n\)。
有一個集合 \(\{1,2,\dots ,n\}\)。你可以進行不超過 \(10^4\) 次操作。每次操作可以是如下三種之一:
- \(\text{A }a\):向互動庫詢問,當前集合裡有多少數是 \(a\) 的倍數。
- \(\text{B }a\):向互動庫詢問,當前集合裡有多少數是 \(a\) 的倍數。然後將所有 \(a\) 的倍數刪去。特別地:\(x\) 將不會被刪去,即使它是 \(a\)
- \(\text{C }a\):向互動庫報告你找出的 \(x\)。
請在規定操作次數內求出 \(x\)。
資料範圍:\(1\leq n\leq 10^5\)。
本題題解
考慮一種暴力做法。列舉 \(1\dots n\) 中每個質數 \(p\),再列舉 \(p\) 在 \(n\) 以下的所有次冪 \(p^k\)。先用 \(\text{B}\) 操作刪除 \(p^k\) 的倍數,再用 \(\text{A}\) 操作查詢集合裡是否仍有 \(p^k\) 的倍數。若有,說明 \(x\) 是 \(p^k\) 的倍數。這樣,我們可以知道每個質數在 \(x\)
設 \(\pi(n)\) 為 \(1\dots n\) 中的質數數量,\(\tau(n)\) 為 \(1\dots n\) 中的質數的冪的數量。打表發現,\(\pi(10^5)=9592,\tau(10^5)=9700\)。上述做法,需要 \(2\tau(n)+1\) 次操作(最後一次操作是報告答案),無法通過本題。
一個小優化是,對每個質數 \(p\),先用 \(\text{B}\) 操作刪除所有 \(p\) 的倍數,再依次詢問所有次冪。操作次數優化為 \(\pi(n)+\tau(n)+1\),但仍無法通過本題。
有一個經典的技巧是,把質數分為 \(\leq \sqrt{n}\)
先用 \(\pi(\sqrt{n})+\tau(\sqrt{n})\leq 238\) 次操作,求出 \(x\) 中所有小質因子及其次冪。設 \(x\) 的小質因子部分為 \(x_{\text{small}}\)。此時,集合裡還剩下 \(1\)、\(x\)、以及所有大質數。我們要確定 \(x\) 裡還剩下的那 \(1\) 個大質因子是誰(或者根本沒有大質因子)。
在前面的樸素做法裡,我們實際對每個大質數用了 \(2\) 次詢問,這樣太多了。考慮減小這個次數。分兩種情況:
- 如果 \(x_{\text{small}} > 1\):我們對每個大質數 \(p\),直接用 \(\text{A}\) 操作詢問 \(p\times x_{\text{small}}\)。如果結果為 \(1\),說明 \(x\) 就等於 \(p\times x_{\text{small}}\)(不然的話,這個數此時應該已經被刪了);如果結果為 \(0\),說明 \(p\) 不是 \(x\) 的大質因子。這樣,對每個大質因子只需要 \(1\) 次操作。找出大質因子所需的操作次數等於大質因子的數量,為:\(\pi(n)-\pi(\sqrt{n})\)。加上小質數的部分,以及報告答案的操作,總次數為 \(\pi(\sqrt{n})+\tau(\sqrt{n})+(\pi(n)-\pi(\sqrt{n}))+1=\pi(n)+\tau(\sqrt{n})+1\leq 9766\)。
- 如果 \(x_{\text{small}}=1\):此時 \(p\times x_{\text{small}}\) 就是大質數 \(p\) 本身,它本來就應該在集合裡,所以直接詢問是沒有意義的。這種情況下,集合裡只剩 \(1\) 和大質數(因為 \(x\) 本身也是 \(1\) 或大質數)。考慮大質數序列分塊。每塊裡,先依次用 \(\text{B}\) 操作將本塊的大質數全部刪除,然後用 \(\text{A}\) 操作對 \(1\) 進行一次詢問(也就是詢問整個集合剩餘的數的數量)。如果發現集合大小減少的量,少於我們刪除的量,說明 \(x\) 就在本塊裡,我們列舉本塊中的數,依次詢問。這樣,設塊的大小為 \(S\),則找出大質數所需的操作次數為:\(\pi(n)-\pi(\sqrt{n})+\lceil\frac{\pi(n)-\pi(\sqrt{n})}{B}\rceil+B\)。取 \(B = \lfloor\sqrt{\pi(n)-\pi(\sqrt{n})}\rfloor = 97\) 時,這部分的最大操作次數為 \(9723\)。加上小質數的部分,以及報告答案的操作,最大總次數為 \(9962\)。可以通過本題。
參考程式碼
// problem: CF1406E
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
const int MAXN = 1e5;
const int SQRT = 316;
const int BLOCK_SIZE = 97;
int ask(int x) {
cout << "A " << x << endl;
int res; cin >> res;
return res;
}
int ask_and_del(int x) {
cout << "B " << x << endl;
int res; cin >> res;
return res;
}
void report_ans(int x) {
cout << "C " << x << endl;
}
int n, ans;
int p[MAXN + 5], cnt_p;
bool v[MAXN + 5];
void sieve(int lim) {
for (int i = 2; i <= lim; ++i) {
if (!v[i]) {
p[++cnt_p] = i;
}
for (int j = 1; j <= cnt_p && p[j] * i <= lim; ++j) {
v[i * p[j]] = 1;
if (i % p[j] == 0) break;
}
}
}
int main() {
cin >> n;
sieve(n);
ans = 1;
// first part:
int second_part_start = cnt_p + 1;
for (int i = 1; i <= cnt_p; ++i) {
if (p[i] > SQRT) {
second_part_start = i;
break;
}
int res = ask_and_del(p[i]);
if (!res) continue;
int lastval = 0, flag = 0;
for (ll j = p[i]; j <= n; j *= p[i]) {
if (!ask(j)) {
ans *= j / p[i];
flag = 1;
break;
}
lastval = j;
}
if (!flag) {
ans *= lastval;
}
} // 238
// second part:
if (ans > 1) {
for (int i = second_part_start; i <= cnt_p; ++i) {
if ((ll)p[i] * ans > n)
continue;
if (ask(p[i] * ans)) {
ans *= p[i];
break;
}
} // 9765
report_ans(ans);
} else {
// 分塊
for (int i = second_part_start; i <= cnt_p; i += BLOCK_SIZE) {
int j = min(cnt_p, i + BLOCK_SIZE - 1);
for (int k = i; k <= j; ++k) {
ask_and_del(p[k]);
}
int res = ask(1);
if (res > 1 + cnt_p - j) {
assert(res == 1 + cnt_p - j + 1);
for (int k = i; k <= j; ++k) {
if (ask(p[k])) {
ans = p[k];
break;
}
}
break;
}
} // 9961
report_ans(ans);
}
return 0;
}