洛谷 P3765 總統選舉 解題報告
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\)
接下來\(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\%\)
對於數據點\(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 總統選舉 解題報告