1. 程式人生 > 實用技巧 >題解 P3765 【總統選舉】

題解 P3765 【總統選舉】

原文連結

很有意思的一道題。

Part.1

一開始拿到這道題一看,題目要求的是區間眾數的出現次數,這個顯然沒法做,據我所知只有靜態分塊或者回滾莫隊可以做不帶修版本的,但是他要我們求的比這個要弱許多,我們可以發現一個性質,顯然如果一個數出現次數超過一半,我們讓它與剩下的數兩兩抵消,剩下的數必然只有它自己。更進一步,如果區間中一旦有一個滿足條件的數,我們任意選不同的兩個數抵消,剩下的必然是那個數。

我們用資料結構實現這個過程。

考慮一棵線段樹,她維護區間中兩兩抵消還剩哪個數以及還剩多少。她的左右兒子資訊合併時,我們看兩個兒子剩下的數是否一樣,如果一樣就相加,如果不一樣就取最大,然後互相抵消。

Part.2

然後,你以為做完了?

如果真做完了那麼它最多是綠題。

我們剛才假設了區間絕對滿足條件,那麼如果區間不滿足條件呢?暴力找

一個顯然的做法是對每一個總統建一個\(vector\),每次找出答案後再\(vector\)中二分詢問區間的左右端點,然後看中間數的個數是否大於\(len/2\)就可以了。

然而題目中有修改。暴力修改

於是實際上,我們要支援一個數據結構,插入一個數,刪除一個數,查詢某個數的排名(相當與二分)

這是什麼?平衡樹板子。

然後,然後就真的完了。

總結一下,我們每次線上段樹上找出一個數,然後再在平衡樹上找出區間中該數的個數檢查是否合法就可以了。

Part.3

安利一下\(LeafyTree\)

,即\(WBLT\),她的形態與線段樹差不多,常數小跑的飛快,一般比\(Splay\)\(FHQTreap\)快兩到三倍。

最後一個細節,在找排名的時候,一路走到葉子後一定要看一下這個數是不是比葉子大,如果小要\(return\;0\),因為這個數可能比平衡樹中的所有數都小。

Code

struct Node
{
    int siz,val;
    Node *lc,*rc;
    Node(int siz,int val,Node *lc,Node *rc) : siz(siz),val(val),lc(lc),rc(rc) {}
    Node() {}
}*null,*stk[maxn*3],utk[maxn*3];
int utot,a[maxn],n;

struct Leafy_Tree
{
    Node *rt[maxn];
    #define newnode(a,b,c,d) (&(*stk[utot++]=Node(a,b,c,d)))
    #define merge(a,b) newnode(a->siz+b->siz,b->val,a,b)
    inline void init()
    {
        for(int i=1;i<=n;++i)
            rt[i]=newnode(1,0x3f3f3f3f,null,null);
    }
    inline void rotate(Node *u)
    {
        if(u->lc->siz > u->rc->siz*ratio)
            u->rc=merge(u->lc->rc,u->rc),stk[--utot]=u->lc,u->lc=u->lc->lc;
        else if(u->rc->siz > u->lc->siz*ratio)
            u->lc=merge(u->lc,u->rc->lc),stk[--utot]=u->rc,u->rc=u->rc->rc;
    }
    inline void pushup(Node *u)
    {
        if(!u->lc->siz) return;
        u->siz=u->lc->siz+u->rc->siz;
        u->val=u->rc->val;
    }
    inline void insert(Node *u,int val)
    {
        if(u->siz==1)
        {
            u->lc=newnode(1,min(u->val,val),null,null);
            u->rc=newnode(1,max(u->val,val),null,null);
        }
        else insert(val > u->lc->val?u->rc:u->lc,val);
        pushup(u);rotate(u);
    }
    inline void erase(Node *u,int val)
    {
        if(u->lc->siz==1&&u->lc->val==val)
            stk[--utot]=u->lc,stk[--utot]=u->rc,*u=*u->rc;
        else if(u->rc->siz==1&&u->rc->val==val)
            stk[--utot]=u->lc,stk[--utot]=u->rc,*u=*u->lc;
        else erase(val > u->lc->val?u->rc:u->lc,val);
        pushup(u);rotate(u);
    }
    inline int rnk(Node *u,int val)
    {
        if(u->siz==1) return val>=u->val;
        return val > u->lc->val ? rnk(u->rc,val)+u->lc->siz : rnk(u->lc,val);
    }
}leafy;

struct Segment_Tree
{
    int mx[maxn],cnt[maxn];
    inline void pushup(int u)
    {
        if(mx[u<<1]==mx[u<<1|1]) mx[u]=mx[u<<1],cnt[u]=cnt[u<<1]+cnt[u<<1|1];
        else if(cnt[u<<1]>cnt[u<<1|1]) mx[u]=mx[u<<1],cnt[u]=cnt[u<<1]-cnt[u<<1|1];
        else mx[u]=mx[u<<1|1],cnt[u]=cnt[u<<1|1]-cnt[u<<1];
    }
    inline void build(int u,int l,int r)
    {
        if(l==r) {mx[u]=a[l],cnt[u]=1;return;}
        int mid=(l+r)>>1;
        build(u<<1,l,mid);build(u<<1|1,mid+1,r);
        pushup(u);
    }
    inline void modify(int u,int l,int r,int x,int val)
    {
        if(l==r) {mx[u]=val;return;}
        int mid=(l+r)>>1;
        if(x<=mid) modify(u<<1,l,mid,x,val);
        else modify(u<<1|1,mid+1,r,x,val);
        pushup(u);
    }
    inline pii query(int u,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y) return make_pair(mx[u],cnt[u]);
        int mid=(l+r)>>1;
        if(y<=mid) return query(u<<1,l,mid,x,y);
        else if(x>mid) return query(u<<1|1,mid+1,r,x,y);
        else
        {
            pii a=query(u<<1,l,mid,x,y),b=query(u<<1|1,mid+1,r,x,y),c;
            if(a.first==b.first) c.first=a.first,c.second=a.second+b.second;
            else if(a.second>b.second) c.first=a.first,c.second=a.second-b.second;
            else c.first=b.first,c.second=b.second-a.second;
            return c;
        }
    }
}seg;

inline void modify(int x,int val)
{
    leafy.erase(leafy.rt[a[x]],x);
    leafy.insert(leafy.rt[val],x);
    seg.modify(1,1,n,x,val);
    a[x]=val;
}

inline int query(int x,int y)
{
    int ans=seg.query(1,1,n,x,y).first;
    if(((y-x+1)>>1)<leafy.rnk(leafy.rt[ans],y)-leafy.rnk(leafy.rt[ans],x-1)) return ans;
    else return -1;
}

template<typename T>
inline void read(T &x)
{
    char c;int f=1;
    while(!isdigit(c=getchar())) (c=='-')&&(f=-1);
    x=c^48;
    while(isdigit(c=getchar())) x=x*10+(c^48);
    x*=f;
}

int main()
{
    int l,r,s,k,m,x;
    for(int i=0;i<maxn*3;++i)
        stk[i]=&utk[i];
    null=new Node(0,0,0,0);
    read(n);read(m);
    for(int i=1;i<=n;++i)
        read(a[i]);
    leafy.init();
    seg.build(1,1,n);
    for(int i=1;i<=n;++i)
        leafy.insert(leafy.rt[a[i]],i);
    while(m--)
    {
        read(l);read(r);read(s);read(k);
        x=query(l,r);
        if(x!=-1) s=x;
        printf("%d\n",s);
        while(k--)
            read(x),modify(x,s);
    }
    printf("%d\n",query(1,n));
    return 0;
}