CF1514D-区间(绝对)众数-莫队、随机化、可持久化线段树

link:https://codeforces.com/contest/1514/problem/D
很久以前小号打的场了,当时D题写的莫队,现在重新来看看。
题意:给一个序列 \([a_1,\dots,a_n]\),有q次询问,每次问:把\([a_l,\dots,a_r]\) 划分最少几个不相交子序列,才能使得每个子序列是beautiful的。称一个序列 \(a_1,\dots,a_x\) 是beautiful的,当且仅当其不存在某个元素的出现次数超过\(\lceil x/2\rceil\)
\(1\leq n,q\leq 3\times 10^5\).


如果某个子区间不存在绝对众数,答案是1,否则假设绝对众数 \(x\) 出现了 \(cnt_x\) 次,则一定要把一些 \(x\) 单独拿出来作为一个序列,假设要拿出来 \(y\) 个,则需要有 \(cnt_x-y\leq (len-cnt_x)+1\),得\(y\geq 2cnt_x -len -1\),此时答案是 \(1+\max(0,2 cnt_x-len-1)\).

所有问题都指向,1、判断区间是否存在绝对众数。2、确定绝对众数的出现次数。后者容易回答,因为绝对众数要么唯一,要么两个出现次数一样,找到某个绝对众数 \(v\) 后,容易通过前缀和 \(O(\log n)\) 地找到。
而如何寻找绝对众数呢?

  • 莫队典中典问题,维护每个值 \(v\) 的出现次数、以及出现次数为 \(c\) 的值有几个,每次改动端点对出现次数的影响至多+1/-1,可以在 \(O(1)\) 的时间内完成更新,这个复杂度 \(O(n\sqrt n)\) 足以通过此题。这个做法不依赖于 “绝对” 众数,只要是众数就可以了。
  • 随机化: 随机 \([l,r]\) 内的一个数字,如果存在绝对众数,则找到的概率不小于 \(1/2\),随机 \(T\) 次,如果存在,则找到的概率至少是 \(p_T=1-(\frac{1}{2})^T\) ,对于 \(q\) 次询问,完全回答正确的概率大约是 \((1-(1/2)^T)^q\approx 1-q\times (1/2)^T\) ,取一个\(T=40\) 左右,就能让正确率达到\(1-10^{-7}\) 左右。
  • 线段树: 还是依赖于其绝对众数的性质,\([l,r]\) 内如果有绝对众数,第 \(\lceil\frac{r-l+1}{2}\rceil =1+\lfloor\frac{r-l}{2}\rfloor\) 大的数一定就是绝对众数。静态区间第 \(k\) 大问题,直接用可持久化线段树完成。
复杂度 适用 修改 实现
莫队 \(O(n\sqrt n)\) 众数 X 容易
随机化 \(O(n\log^2 n)\) 绝对众数 X 最容易
线段树 \(O(n\log n)\) 绝对众数 容易
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define dbg(x) cout<<#x"="<<x<<' '
using namespace std;
const int N=3e5+5;
int n,q,a[N],rt[N];
vector<vector<int>> pos;
struct Node{int v,ls,rs;};
template<int LOG=20>
class SegT{
    Node tr[N*LOG];
    int cnt_node;
public:
    void modify(int &x,int y,int l,int r,int p,int v){
        tr[x=++cnt_node]=tr[y];
        if(l==r){
            tr[x].v+=v;
            return;
        }
        int mid=l+(r-l>>1);
        if(mid>=p)modify(tr[x].ls,tr[y].ls,l,mid,p,v);
        else modify(tr[x].rs,tr[y].rs,mid+1,r,p,v);
        tr[x].v=tr[tr[x].ls].v+tr[tr[x].rs].v;
    }
    int query(int x,int y,int l,int r,int k){
        if(l==r)return l;
        int mid=l+(r-l>>1);
        int s=tr[tr[y].ls].v-tr[tr[x].ls].v;
        if(s>=k)return query(tr[x].ls,tr[y].ls,l,mid,k);
        return query(tr[x].rs,tr[y].rs,mid+1,r,k-s);
    }
};
SegT tr;
int main(){
    fastio;
    cin>>n>>q;
    pos=vector<vector<int>>(n+1);
    rep(i,1,n){
        cin>>a[i];
        pos[a[i]].push_back(i);
        tr.modify(rt[i],rt[i-1],1,n,a[i],1);
    }
    while(q--){
        int l,r,count,v;
        cin>>l>>r;
        v=tr.query(rt[l-1],rt[r],1,n,1+(r-l)/2);
        auto L=lower_bound(pos[v].begin(),pos[v].end(),l);
        auto R=prev(upper_bound(pos[v].begin(),pos[v].end(),r));
        if(L==pos[v].end())count=0;
        else count=(R-L)+1;
        if(2*count>=r-l+1)cout<<1+max(0,2*count-(r-l+1)-1)<<endl;
        else cout<<1<<endl;
    }
    return 0;
}
posted @ 2024-03-18 18:52  yoshinow2001  阅读(10)  评论(0编辑  收藏  举报