1. 程式人生 > >【bzoj4966】總統選舉 隨機化+線段樹

【bzoj4966】總統選舉 隨機化+線段樹

編號 並且 bsp 出現 ++ for 條件 amp 描述

題目描述

黑惡勢力的反攻計劃被小C成功摧毀,黑惡勢力只好投降。秋之國的人民解放了,舉國歡慶。此時,原秋之國總統因沒能守護好國土,申請辭職,並請秋之國人民的大救星小C欽定下一任。作為一名民主人士,小C決定舉行全民大選來決定下一任。為了使最後成為總統的人得到絕大多數人認同,小C認為,一個人必須獲得超過全部人總數的一半的票數才能成為總統。如果不存在符合條件的候選人,小C只好自己來當臨時大總統。為了盡可能避免這種情況,小C決定先進行幾次小規模預選,根據預選的情況,選民可以重新決定自己選票的去向。由於秋之國人數較多,統計投票結果和選票變更也成為了麻煩的事情,小C找到了你,讓你幫他解決這個問題。 【問題描述】秋之國共有n個人,分別編號為1,2,…,n,一開始每個人都投了一票,範圍1~n,表示支持對應編號的人當總統。共有m次預選,每次選取編號[li,ri]內的選民展開小規模預選,在該區間內獲得超過區間大小一半的票的人獲勝,如果沒有人獲勝,則由小C欽定一位候選者獲得此次預選的勝利(獲勝者可以不在該區間內),每次預選的結果需要公布出來,並且每次會有ki個人決定將票改投向該次預選的獲勝者。全部預選結束後,公布最後成為總統的候選人

輸入

第一行兩個整數n,m,表示秋之國人數和預選次數。 第二行n個整數,分別表示編號1~n的選民投的票。 接下來m行,每行先有4個整數,分別表示li,ri,si,ki,si表示若此次預選無人勝選,視作編號為si的人獲得勝利 接下來ki個整數,分別表示決定改投的選民。 1<=n,m<=500,000,Σki<=1,000,000,1<=li<=ri<=n,1<=si<=n。

輸出

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

樣例輸入

5 4
1 2 3 4 5
1 2 1 1 3
5 5 1 2 2 4

2 4 2 0
3 4 2 1 4

樣例輸出

1
5
5
2
-1


題解

隨機化+線段樹

考慮如果區間中一個數的出現次數等於區間長度的一半,那麽期望隨機找兩次即可找到該數。

所以理論上看,每次隨機找20次,完全正確地處理500000個詢問的概率約為0.62。而實際上由於數據水,隨機15次即可AC。

然後就是找某數在區間中出現的次數,直接對每個數開一棵線段樹即可。

時間復雜度$O(15n\log n)$,實際上本題很卡時(卡隨機化),需要使用結構體寫線段樹才可以卡過。

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define N 500010
#define lson l , mid , a[x].ls
#define rson mid + 1 , r , a[x].rs
using namespace std;
struct data
{
	int ls , rs , si;
}a[N * 60];
int w[N] , root[N] , tot;
inline int read()
{
    int ret = 0; char ch = getchar();
    while(ch < ‘0‘ || ch > ‘9‘) ch = getchar();
    while(ch >= ‘0‘ && ch <= ‘9‘) ret = ret * 10 + ch - ‘0‘ , ch = getchar();
    return ret;
}
void update(int p , int v  , int l , int r , int &x)
{
    if(!x) x = ++tot;
    a[x].si += v;
    if(l == r) return;
    int mid = (l + r) >> 1;
    if(p <= mid) update(p , v , lson);
    else update(p , v , rson);
}
int query(int b , int e , int l , int r , int x)
{
    if(!x) return 0;
    if(b <= l && r <= e) return a[x].si;
    int mid = (l + r) >> 1 , ans = 0;
    if(b <= mid) ans += query(b , e , lson);
    if(e > mid) ans += query(b , e , rson);
    return ans;
}
int main()
{
    srand(2333666);
    int n , m , i , l , r , s , k , x , p , t;
    n = read() , m = read();
    for(i = 1 ; i <= n ; i ++ ) w[i] = read() , update(i , 1 , 1 , n , root[w[i]]);
    while(m -- )
    {
        l = read() , r = read() , s = read() , k = read() , p = 0;
        for(i = 1 ; i <= 15 ; i ++ )
        {
            t = w[rand() % (r - l + 1) + l];
            if(query(l , r , 1 , n , root[t]) > (r - l + 1) >> 1)
            {
                p = t;
                break;
            }
        }
        if(!p) p = s;
        printf("%d\n" , p);
        for(i = 1 ; i <= k ; i ++ ) x = read() , update(x , -1 , 1 , n , root[w[x]]) , update(x , 1 , 1 , n , root[p]) , w[x] = p;
    }
    p = -1;
    for(i = 1 ; i <= 15 ; i ++ )
    {
        t = w[rand() % n + 1];
        if(a[root[t]].si > n >> 1)
        {
            p = t;
            break;
        }
    }
    printf("%d\n" , p);
    return 0;
}

【bzoj4966】總統選舉 隨機化+線段樹