1. 程式人生 > >洛谷 P3765 總統選舉 解題報告

洛谷 P3765 總統選舉 解題報告

平衡樹 開始 等於 輸入 解決 lin ret 格式 .com

P3765 總統選舉

題目背景

黑惡勢力的反攻計劃被小C成功摧毀,黑惡勢力只好投降。秋之國的人民解放了,舉國歡慶。此時,原秋之國總統因沒能守護好國土,申請辭職,並請秋之國人民的大救星小C欽定下一任。作為一名民主人士,小C決定舉行全民大選來決定下一任。為了使最後成為總統的人得到絕大多數人認同,小C認為,一個人必須獲得超過全部人總數的一半的票數才能成為總統。如果不存在符合條件的候選人,小C只好自己來當臨時大總統。為了盡可能避免這種情況,小C決定先進行幾次小規模預選,根據預選的情況,選民可以重新決定自己選票的去向。由於秋之國人數較多,統計投票結果和選票變更也成為了麻煩的事情,小C找到了你,讓你幫他解決這個問題。

題目描述

秋之國共有\(n\)個人,分別編號為\(1,2,…,n\),一開始每個人都投了一票,範圍\(1\)~\(n\),表示支持對應編號的人當總統。共有\(m\)次預選,每次選取編號\([l_i,r_i]\)內的選民展開小規模預選,在該區間內獲得超過區間大小一半的票的人獲勝,如果沒有人獲勝,則由小C欽定一位候選者獲得此次預選的勝利(獲勝者可以不在該區間內),每次預選的結果需要公布出來,並且每次會有\(k_i\)個人決定將票改投向該次預選的獲勝者。全部預選結束後,公布最後成為總統的候選人。

輸入輸出格式

輸入格式:

第一行兩個整數\(n,m\),表示秋之國人數和預選次數。

第二行\(n\)個整數,分別表示編號\(1\)

~\(n\)的選民投的票。

接下來\(m\)行,每行先有\(4\)個整數,分別表示\(l_i,r_i,s_i,k_i\)\(s_i\)表示若此次預選無人勝選,視作編號為\(s_i\)的人獲得勝利,接下來\(k_i\)個整數,分別表示決定改投的選民。

輸出格式:

\(m+1\)行,前\(m\)行表示各次預選的結果,最後一行表示最後成為總統的候選人,若最後仍無人勝選,輸出\(-1\)

說明

對於前\(20\%\)的數據,\(1 \leq n,m \leq 5000\)

對於前\(40\%\)的數據,\(1 \leq n,m \leq 50000\)\(\sum k_i \leq 50000\)

對於前\(50\%\)

的數據,\(1 \leq n,m \leq 100000\)\(\sum k_i \leq 200000\)

對於數據點\(6\)~\(7\),保證所有選票始終在\(1\)~\(10\)之間。

對於\(100\%\)的數據,\(1 \leq n,m \leq 500,000\)\(\sum k_i \leq 10^6\)\(1 \leq l_i \leq r_i \leq n\)\(1 \leq s_i \leq n\)


好題啊

區間求眾數一般情況下只能用分塊之類的求解

但是這題當選有條件啊,大於區間一半哎,肯定想辦法在這裏下手

bzoj有道題叫made,可以從這裏得到啟發

這題是這麽做的呢

具體的說,我們從左向右掃描,假設當前可能成為答案的為\(v\),且它出現了\(cnt\)次,如果當前數等於\(x\),則\(++cnt\),否則\(--cnt\),若\(cnt==0\),則替換\(x\)為當前數,\(cnt=1\)

這是什麽原理呢?可以理解為減小了整個區間的抵消,因為要求出現一半以上,所以當一對不一樣的數出現時,可以把這一對刪掉,減小區間規模

可以這樣做得到的數不一定是出現超過一半的,只是它最有可能

我們可以拿線段樹模擬這個區間合並的過程,維護\(v\)\(cnt\),直接區間查詢,單點修改

如何檢驗是否出現超過一半呢?對每一個候選人,我們搞一顆平衡樹維護,以位置為BST性質,這樣直接把區間分離出來看看大小就行了


Code:

#include <cstdio>
#include <cstdlib>
#define ls ch[now][0]
#define rs ch[now][1]
const int N=2e6+10;
int bvote[N<<2],cnt[N<<2],vote[N],n,m;
void updata(int id)
{
    if(bvote[id<<1]==bvote[id<<1|1])
    {
        cnt[id]=cnt[id<<1]+cnt[id<<1|1];
        bvote[id]=bvote[id<<1];
        return;
    }
    if(cnt[id<<1]>cnt[id<<1|1])
    {
        cnt[id]=cnt[id<<1]-cnt[id<<1|1];
        bvote[id]=bvote[id<<1];
    }
    else
    {
        cnt[id]=cnt[id<<1|1]-cnt[id<<1];
        bvote[id]=bvote[id<<1|1];
    }
}
void change(int id,int l,int r,int pos,int v)
{
    if(l==r)
    {
        bvote[id]=v;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid) change(id<<1,l,mid,pos,v);
    else change(id<<1|1,mid+1,r,pos,v);
    updata(id);
}
int query(int id,int l,int r,int L,int R,int &ct)
{
    if(l==L&&r==R)
    {
        ct=cnt[id];
        return bvote[id];
    }
    int Mid=L+R>>1;
    if(r<=Mid) return query(id<<1,l,r,L,Mid,ct);
    else if(l>Mid) return query(id<<1|1,l,r,Mid+1,R,ct);
    else
    {
        int lv,rv,lc,rc,nv;
        lv=query(id<<1,l,Mid,L,Mid,lc),rv=query(id<<1|1,Mid+1,r,Mid+1,R,rc);
        if(lv==rv) {ct=lc+rc;return lv;}
        if(lc>rc) nv=lv,ct=lc-rc;
        else nv=rv,ct=rc-lc;
        return nv;
    }
}
void build(int id,int l,int r)
{
    if(l==r)
    {
        cnt[id]=1,bvote[id]=vote[l];
        return;
    }
    int mid=l+r>>1;
    build(id<<1,l,mid),build(id<<1|1,mid+1,r);
    updata(id);
}
int ch[N][2],siz[N],val[N],dat[N],tot,root[N];
void updata2(int now){siz[now]=siz[ls]+siz[rs]+1;}
void split(int now,int k,int &x,int &y)
{
    if(!now){x=y=0;return;}
    if(dat[now]<=k)
        x=now,split(rs,k,rs,y);
    else
        y=now,split(ls,k,x,ls);
    updata2(now);
}
int Merge(int x,int y)
{
    if(!x||!y) return x+y;
    if(val[x]<val[y])
    {
        ch[x][1]=Merge(ch[x][1],y);
        updata2(x);
        return x;
    }
    else
    {
        ch[y][0]=Merge(x,ch[y][0]);
        updata2(y);
        return y;
    }
}
int New(int k)
{
    val[++tot]=rand(),siz[tot]=1,dat[tot]=k;
    return tot;
}
void Insert(int id,int k)
{
    int x,y;
    split(root[id],k,x,y);
    root[id]=Merge(x,Merge(New(k),y));
}
void extrack(int id,int k)
{
    int x,y,z;
    split(root[id],k,x,y);
    split(x,k-1,x,z);
    root[id]=Merge(x,y);
}
int ask(int id,int l,int r)
{
    int x,y,z,s;
    split(root[id],l-1,x,y);
    split(y,r,z,y);
    s=siz[z];
    root[id]=Merge(x,Merge(z,y));
    return s;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",vote+i),Insert(vote[i],i);
    build(1,1,n);
    for(int l,r,s,k,p,ct,i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&l,&r,&s,&k);
        int naive=query(1,l,r,1,n,ct);
        if(ask(naive,l,r)>(r+1-l>>1)) s=naive;
        printf("%d\n",s);
        for(int j=1;j<=k;j++)
        {
            scanf("%d",&p);
            extrack(vote[p],p);
            vote[p]=s;
            Insert(vote[p],p);
            change(1,1,n,p,vote[p]);
        }
    }
    int nai=bvote[1];
    if((ask(nai,1,n)<<1)>n) printf("%d\n",nai);
    else printf("-1\n");
    return 0;
}

2018.9.5

洛谷 P3765 總統選舉 解題報告