洛谷 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);
}
posted @ 2026-01-10 16:04  xiaoniu142857  阅读(8)  评论(0)    收藏  举报