洛谷 P9683 A Certain Forbidden Index 题解
題目鏈接:\(\color{Purple}\texttt{P9683 A Certain Forbidden Index}\)。
填坑。提供一個(gè)相對(duì)好寫的做法。
考慮把一堆不交的區(qū)間綁在一起問(即先詢問這些區(qū)間的并,判斷答案是否在里面):對(duì)于一個(gè)區(qū)間,能與它綁在一起的區(qū)間最多只有 \(1\) 個(gè)是與它同一層的。
于是我們想到一個(gè)比較原始的做法:因?yàn)?\([1,2^k]\) 沒有任何區(qū)間可以合并,所以放最后作為單獨(dú)一塊問;從線段樹的頂端按照 BFS 序往下詢問,如果該區(qū)間 \([l,r]\) 沒有進(jìn)行過標(biāo)記(記這種區(qū)間為“初始區(qū)間”),按以下方式處理:標(biāo)記它,如果它是作為左兒子被問到的(反之同理),那么就不斷往右找下一層且它們的并集是一個(gè)區(qū)間的區(qū)間 \([l_1,r_1]\)(即滿足 \(l_1=r+1\)),然后 \([l,r]\leftarrow[l_1,r_1]\),對(duì) \([l_1,r_1]\) 進(jìn)行標(biāo)記;這樣能獲得不少的塊并且答案已經(jīng)很優(yōu)。加上對(duì) \(k=1\) 的特判可以做到 \(63\mathrm{pts}\):筆者考場(chǎng)上使用的是該做法,已經(jīng)得到了一個(gè)非常可觀的分?jǐn)?shù)。
注:對(duì)于這種做法,如果對(duì)塊內(nèi)相鄰元素連邊,在整棵線段樹上這些邊就可以構(gòu)成很多交叉的 X 型(讀者可以自行理解一下)。
但是此時(shí)答案還不是最優(yōu)的。考慮是否可以合并一些區(qū)間:我們觀察到,對(duì)于一些左 / 右端點(diǎn)在正中間的作為右 / 左兒子進(jìn)行處理的區(qū)間,是可以將它所在的塊和同一層那個(gè)唯一可以合并的區(qū)間所在的塊合并的。例如,\(k=4\) 時(shí),區(qū)間 \([7,8]\) 與區(qū)間 \([9,10]\) 所在的塊可以合并。處理的過程中考慮一下這種情況。
事實(shí)上我們可以觀察到,對(duì)于合并同一層的這種情況,我們只需處理 \([l,r]\) 滿足 \(r<2^k\) 且作為右兒子詢問到的“初始區(qū)間”所在的塊和它右邊那個(gè)同層的且可合并的區(qū)間所在的塊即可。證明顯然。
詢問塊內(nèi)元素的過程可以不用像其他題解一樣用二分,因?yàn)槲覀冇^察到如果一個(gè)塊的大小如果不超過 \(2\),那么二分跟樸素做法的沒有區(qū)別——實(shí)際上塊越小兩種做法的區(qū)別越小。又因?yàn)槭前凑?BFS 序從上往下問,感性理解一下就可以得知越往下問塊越小而且數(shù)量越多。實(shí)測(cè)可以獲得 \(100\mathrm{pts}\)。
如果不放心可以把所有塊全部預(yù)處理出來然后按照塊的大小降序排序詢問。但是這個(gè)做法會(huì)在 \(k=14\) 的時(shí)候超時(shí),所以選用前者。
示例代碼(\(100\mathrm{pts}\)):
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
int query(int,int);
pii solve(int k){
vector<tuple<int,int,int,int> > q;
function<void(int,int,int)> dfs=[&](int l,int r,int w){
if(l==r)return;
q.emplace_back(l,l+r>>1,w-1,0);
q.emplace_back(l+r+1>>1,r,w-1,1);
dfs(l,l+r>>1,w-1);
dfs(l+r+1>>1,r,w-1);
}; // 按照 BFS 序處理出所有區(qū)間遍歷順序,0/1 表示左 / 右兒子
dfs(1,1<<k,k);
set<pii> t; t.emplace(1,1<<k);
vector<tuple<vector<pii>,int,int,int,int> > s;
for(auto [l,r,w,b]:q){
if(t.find(make_pair(l,r))==t.end()){
vector<pii> v; int p=(b?l:r);
for(int i=w-1;i>=0;i--){
v.emplace_back(b?p-(1<<i):p+1,b?p-1:p+(1<<i));
b?p-=(1<<i):p+=(1<<i);
} // 處理塊
if(b){
bool b2=false;
if(r<(1<<k)){
b2=true,p=r;
for(int i=w;i>=0;i--){
v.emplace_back(p+1,p+(1<<i));
p+=(1<<i);
}
} // 可以與同一層的合并
if(query(l-(1<<w)+1,b2?(r<<1)-l+(1<<w):r)){
// 詢問是否在整個(gè)區(qū)間,下同
for(auto [l1,r1]:v)
if(query(l1,r1))return make_pair(l1,r1);
// 詢問單個(gè)區(qū)間,下同
return make_pair(l,r);
// 不在上述區(qū)間就是最后一個(gè),下同
}
}
else if(query(l,r+(1<<w)-1)){
for(auto [l1,r1]:v)
if(query(l1,r1))return make_pair(l1,r1);
return make_pair(l,r);
}
for(auto [l1,r1]:v)t.emplace(l1,r1);
t.emplace(l,r); // 使用 std::set 打標(biāo)記
}
}
return make_pair(1,1<<k); // 都不是上述區(qū)間
}
總結(jié)
以上是生活随笔為你收集整理的洛谷 P9683 A Certain Forbidden Index 题解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 Docker Compose V2
- 下一篇: [译] kubernetes:kube-