洛谷 P14538 [OII 2025] 市政委员会 / Giunta comunale 题解
Solution
考虑分治,并不断缩小答案的查找范围。维护当前下标集合 \(I\) 和它对应的数值集合 \(V=\{a_i|i\in I\}\)。
将当前范围分成左右两半,下标集合分别为 \(I_l\) 和 \(I_r\)。先处理出所有在左边出现过的数 \(V_l\)。
此时如果 \(|I_l|>|V_l|\),说明答案在左侧,向左递归。否则判断 \(V_l\) 中的每个数是否在右侧出现过。如果有则找到答案。否则向右递归。
发现这样询问次数上界为 \(2N+N+\frac{N}{2}+\frac{N}{4}+\dots=4N\),需要优化。
注意到如果最终向左递归,则询问 \(|I_l|\) 次。否则询问 \(2|I_l|\) 次。因此我们希望多一些向左递归。考虑平衡询问数和层数,让左边分到 \(k\cdot |I|\) 个下标。
实验法得知,\(k=0.62\) 时刚好通过本题。
Code
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i(a);i<b;++i)
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define eb emplace_back
using namespace std;
bool chiedi(vector<int> S,int x);
int calc(vector<int> id,vector<int> val){
int mid=max(1,int(id.size()*0.62));
vector<int> lId,rId,lVal,rVal;
rep(i,0,mid) lId.eb(id[i]);
rep(i,mid,id.size()) rId.eb(id[i]);
for(int x:val){
if(chiedi(lId,x)) lVal.eb(x);
else rVal.eb(x);
}
if(lId.size()>lVal.size()) return calc(lId,lVal);
for(int x:lVal) if(chiedi(rId,x)) return x;
return calc(rId,rVal);
}
int delibera(int n){
vector<int> id,val;
rept(i,0,n) id.eb(i);
rep(i,0,n) val.eb(i);
return calc(id,val);
}

浙公网安备 33010602011771号