P3795 总统选举 题解

P3795 总统选举 题解

题目链接

题目大意

\(n\) 个人,每个人都要投一票。有 \(m\) 轮操作,每次求在区间 \([l_i,r_i]\) 里面有没有人被投票数超过人数的一半,输出这个数,并使 \(k_i\) 个人把自己的票投给获胜者。如果不存在人满足条件,则让 \(c_i\) 获胜。最后输出整个区间内超过区间一半的人。没有输出 \(-1\)

前置知识

线段树,平衡树,(随机化),摩尔投票法

摩尔投票法

模版

给定一个可重集合,求这个序列的众数。保证这个众数的数量大于集合大小的一半。

我们考虑这个限制条件,发现数量大于集合大小的一半的数有且只有一个。

考虑存在一个小黑屋,其中我们把众数放进去,然后让集合里面的其他数一一去和他抵消。如果这个众数的大小大于集合的一半,我们发现其一定无法抵消掉里面所有数,一定会有数剩下。

考虑依次加数,显然最后剩下的一定是众数。

cin>>n;
for(int i=1;i<=n;i++)
{
    int x;
    cin>>x;
    if(now==x)  cnt++;
    if(cnt==0)  now=x,cnt++;
    if(x!=now)  cnt--;
}
cout<<now<<endl;

考虑分析时间复杂度,空间复杂度,发现时间复杂度显然是 \(O(n)\) 的,而空间连数组都不用看,只需要 \(O(1)\) 的空间即可。

本题做法

找到可能的众数

线段树

发现众数修改,考虑维护众数。考虑摩尔投票的性质,发现其显然满足区间可加性,考虑线段树维护。

具体来说,考虑维护两个信息,一个是当前区间的众数 \(num\),一个是当前区间众数出现净次数 \(cnt\)(指摩尔排序后留下的众数个数)。

如果两个子区间众数相同,则直接把净次数相加即可,否则,大减小即可。

随机化

每次在区间中随机取 \(K\) 个数,然后判断这个数是不是区间众数。每次找不到小于众数的概率为 $\frac{1}{2} \(,\)k$ 次后还找不到众数的概率为 $\frac{1}{2^k} $(如果存在众数)。直接做即可。

判断是否是答案:平衡树

考虑通过线段树找到了答案,但是我们是通过摩尔投票找到的答案,而摩尔投票的前提是众数数大于当前集合大小一半。考虑判断该条件。显然,如果这个数不满足条件,即不满足摩尔投票的条件,肯定也不满足题目中的条件。

考虑平衡树实现。我们可以对于每一个人开一棵平衡树,每个人的平衡树里面存支持他的人的编号。对于一个区间 \([l,r]\),我们可以找到编号为 \([1,r]\) 之间的人支持找到众数的数量,也可以找到编号为 \([1,l-1]\) 之间的人支持众数的数量。显然不存在相同的数不同的众数,其本质就可以转化为找排名。

CODE

线段树+平衡树(Splay)

我Splay动态开点开了10倍空间,因为有删除操作,理论上来说写一个空间回收空间可以开的小一点。

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N=5e5+100,INF=1e9;

int n,m;
int dt[N];

struct SegmentTree{
    #define l(x) x*2
    #define r(x) x*2+1

    struct NODE{
        int cnt,num;
    }tr[N*4];

    void pushup(int p)
    {
        if(tr[l(p)].num==tr[r(p)].num)  tr[p].num=tr[l(p)].num,tr[p].cnt=tr[l(p)].cnt+tr[r(p)].cnt;
        else
            if(tr[l(p)].cnt>=tr[r(p)].cnt)  tr[p].cnt=tr[l(p)].cnt-tr[r(p)].cnt,tr[p].num=tr[l(p)].num;
            else tr[p].cnt=tr[r(p)].cnt-tr[l(p)].cnt,tr[p].num=tr[r(p)].num;
    }

    void build(int p,int l,int r)
    {
        if(l==r)    return tr[p].cnt=1,tr[p].num=dt[l],void();
        int mid=l+r>>1;
        build(l(p),l,mid),build(r(p),mid+1,r);
        pushup(p);
    }

    void change(int p,int l,int r,int x,int w)
    {
        if(l==x && r==x)    return tr[p].num=w,tr[p].cnt=1,void();
        int mid=l+r>>1;
        if(x<=mid)  change(l(p),l,mid,x,w);
        if(x>mid)   change(r(p),mid+1,r,x,w);
        pushup(p);
    }

    NODE ask(int p,int l,int r,int x,int y)
    {
        if(l==x && r==y)    return tr[p];
        int mid=l+r>>1;
        if(mid>=y)  return ask(l(p),l,mid,x,y);
        else if(mid<x)  return ask(r(p),mid+1,r,x,y);
        else
        {
            NODE a=ask(l(p),l,mid,x,mid),b=ask(r(p),mid+1,r,mid+1,y);
            NODE c;
            if(a.num==b.num)    c.num=a.num,c.cnt=a.cnt+b.cnt;
            else
                if(a.cnt>=b.cnt)    c.cnt=a.cnt-b.cnt,c.num=a.num;
                else    c.cnt=b.cnt-a.cnt,c.num=b.num;
            return c;
        }
    }
}Tr_seg;

struct Splay{
    struct NODE{
        int s[2],p,v;
        int size;

        void init(int _p,int _v)
        {
            p=_p,v=_v;
            size=1;
        }
    }tr[N*10];

    int root[N],idx;
    int L[N];

    void pushup(int x)
    {
        tr[x].size=tr[tr[x].s[0]].size+tr[tr[x].s[1]].size+1;
    }

    void rotate(int x)
    {
        int y=tr[x].p,z=tr[y].p;
        int k=tr[y].s[1]==x;
        tr[z].s[tr[z].s[1]==y]=x,tr[x].p=z;
        tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
        tr[x].s[k^1]=y,tr[y].p=x;
        pushup(y),pushup(x);
    }

    void splay(int x,int k,int b)
    {
        while(tr[x].p!=k)
        {
            int y=tr[x].p,z=tr[y].p;
            if(z!=k)
                if((tr[y].s[0]==x)^(tr[z].s[0]==y)) rotate(x);
                else rotate(y);
            rotate(x);
        }
        if(!k)  root[b]=x;
    }

    int insert(int v,int b)
    {
        int u=root[b],p=0;
        while(u) p=u,u=tr[u].s[v>tr[u].v];
        u=++idx;
        if(p)   tr[p].s[v>tr[p].v]=u;
        tr[u].init(p,v);
        splay(u,0,b);
        return u;
    }

    void find(int x,int b)
    {
        int u=root[b];
        if(!u)  return;
        while(tr[u].s[x>tr[u].v] && tr[u].v!=x) u=tr[u].s[x>tr[u].v];
        splay(u,0,b);
    }

    int next(int x,int f,int b)
    {
        find(x,b);
        int u=root[b];
        if((tr[u].v>x && f==1) || (tr[u].v<x && f==0))  return u;
        u=tr[u].s[f];
        while(tr[u].s[f^1]) u=tr[u].s[f^1];
        return u;
    }

    int get(int v,int b)
    {
        find(v,b);
        if(tr[root[b]].v!=v)    v=tr[next(v,0,b)].v;
        int u=root[b],ans=0;
        while(true)
        {
            if(tr[u].v>v)   u=tr[u].s[0];
            else if(tr[u].v==v)
                return ans+=tr[tr[u].s[0]].size,splay(u,0,b),ans;
            else    ans+=tr[tr[u].s[0]].size+1,u=tr[u].s[1];
        }
    }


    void remove(int x,int b)
    {
        int L=next(x,0,b),R=next(x,1,b);
        splay(L,0,b),splay(R,L,b);
        tr[R].s[0]=0;
        pushup(L),pushup(R);
    }
}Tr_Splay;

signed main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        Tr_Splay.L[i]=Tr_Splay.insert(-INF,i),Tr_Splay.insert(INF,i);
    for(int i=1;i<=n;i++)
        cin>>dt[i],Tr_Splay.insert(i,dt[i]);
    Tr_seg.build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int l,r,s,q;
        cin>>l>>r>>s>>q;
        int x=Tr_seg.ask(1,1,n,l,r).num;
        int sum=Tr_Splay.get(r,x)-Tr_Splay.get(l-1,x);

        if(sum>(r-l+1)/2)   s=x;
        for(int j=1;j<=q;j++)
        {
            cin>>x;
            Tr_seg.change(1,1,n,x,s);
            Tr_Splay.remove(x,dt[x]);
            dt[x]=s;
            Tr_Splay.insert(x,dt[x]);
        }
        cout<<s<<endl;
    }
    int x=Tr_seg.ask(1,1,n,1,n).num;
    int sum=Tr_Splay.get(n,x);
    if(sum>n/2) cout<<x<<endl;
    else        cout<<-1<<endl;
    return 0;
}

随机化+pb_ds

pb_ds好闪!五分钟写完了我今天调了一天的Splay!

#include<bits/stdc++.h>
#include<bits/extc++.h>
#define LL long long

using namespace __gnu_pbds;
using namespace std;

const int N=5e5+100;

int n,m;
int a[N];
tree<LL,null_type,less<LL>,rb_tree_tag,tree_order_statistics_node_update> S[N];
//平衡树
int p[N];

int calc(int x,int l,int r)
{
    return S[x].order_of_key(r+1)-S[x].order_of_key(l);
}

int solve(int l,int r)
{
    int len=r-l+1;
    for(int i=1;i<=20;i++)
    {
        int x=rand()*rand()%len+l;
        x=a[x];
        if(calc(x,l,r)>len/2)   return x;
    }
    return -1;
}

signed main()
{
    srand((unsigned)time(0));
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i],S[a[i]].insert(i);
    for(int i=1;i<=m;i++)
    {
        int l,r,s,k;
        cin>>l>>r>>s>>k;
        for(int j=1;j<=k;j++)   cin>>p[j];
        int ans=solve(l,r);
        if(ans==-1) ans=s;
        cout<<ans<<endl;
        for(int j=1;j<=k;j++)
        {
            S[a[p[j]]].erase(p[j]);
            a[p[j]]=ans;
            S[ans].insert(p[j]);
        }
    }
    cout<<solve(1,n)<<endl;
    return 0;
}

posted @ 2025-05-26 19:44  袍蚤  阅读(33)  评论(0)    收藏  举报