洛谷 P3765 总统选举(线段树+treap)

来源:互联网 发布:java gb2312转utf8 编辑:程序博客网 时间:2024/05/17 23:40

题目背景

黑恶势力的反攻计划被小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个整数,分别表示决定改投的选民。

输出格式:

共m+1行,前m行表示各次预选的结果,最后一行表示最后成为总统的候选人,若最后仍无人胜选,输出-1。


输入输出样例

输入样例#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:

1
5
5
2
-1


这里写图片描述


题解

这题呢,是一道线段树的大好题。首先我们明确题目就是想求一个区间内是否有出现次数大于区间长度一半的数,如果有,我想知道它是多少。这就是BZOJ 2456找众数那题的强化版。

题目不同之处在于支持修改,所以需要线段数维护,为什么线段树可以呢?
因为线段数能维护的东西有两点性质:

①维护的信息满足区间加法。就是说一个区间分为左右两个区间,这两个区间的信息是可加(即合并)的。例如区间和等于左区间和与右区间和的和,区间最值等于左区间最值与右区间最值的最值……
②能直接维护的信息一定是个封闭的域,即可以自己维护自己。这貌似是一句废话,我们明确知道当前区间一定能够维护想维护信息的准确结果。例如我们可以维护最值与和,因为这与其他区间无关,我们也可以维护区间内1的个数,也可以维护从左开始的最长连续的1的个数,从右边开始也行,但是不能直接维护整个区间的最长连续的1的个数。为什么呢?因为当两个区间合并时,就无法维护跨越两个区间的那部分了,就是说有一部分维护不了了。就这么简单,所以才要开三个数组来维护这个东西。

言归正传,这题我们可以用线段树维护val,cnt(定义和找众数一样),支持询问和修改。维护的具体细节就是两段val相同,cnt加起来,两段val不同cnt就大减小并将val设为cnt大的那段的val。

这样这题就做完了吗?并不。因为“众数”不一定存在,就是说未必有满足题目的答案,我们维护出的未必是正确答案,只是维护的那个数是“最具有成为‘众数’的潜质”的数罢了。所以和之前不同的,我们要开一个平衡树检验答案的正确性。很简单,就每个人开个treap判断在[l,r]内有几个人选他就行了。这只需要在treap上进行普通的二叉查找就行了。
时间复杂度O(Σki * logn)。


代码

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <algorithm>#define N 500010#define M 1000010using namespace std;int n, m, cur = 0, person, org[N];struct Tnode{    int val, cnt;    Tnode() {}    Tnode(int _val, int _cnt):val(_val), cnt(_cnt) {}}tree[N<<2];struct Treap{    Treap *lson, *rson;    int val, fix, size;    int lsize(){return lson ? lson->size : 0;}    int rsize(){return rson ? rson->size : 0;}    void Recount(){size = lsize() + rsize() + 1;}}*Root[N], Node[M+N];void Up(int root, int Lson, int Rson){    if(tree[Lson].val == tree[Rson].val)      tree[root] = Tnode(tree[Lson].val, tree[Lson].cnt + tree[Rson].cnt);    else if(tree[Lson].cnt > tree[Rson].cnt)      tree[root] = Tnode(tree[Lson].val, tree[Lson].cnt - tree[Rson].cnt);    else      tree[root] = Tnode(tree[Rson].val, tree[Rson].cnt - tree[Lson].cnt);}void build(int root, int L, int R){    if(L == R){      tree[root] = Tnode(org[L], 1);      return;     }    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;    build(Lson, L, mid);    build(Rson, mid+1, R);    Up(root, Lson, Rson);}void update(int root, int L, int R, int x, int y, int v){    if(x > R || y < L)  return;    if(x <= L && y >= R){      tree[root] = Tnode(v, 1);      return;    }    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;    update(Lson, L, mid, x, y, v);    update(Rson, mid+1, R, x, y, v);    Up(root, Lson, Rson);}Tnode query(int root, int L, int R, int x, int y){    if(x > R || y < L)  return Tnode(0, 0);    if(x <= L && y >= R)  return tree[root];    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;    Tnode ans1 = query(Lson, L, mid, x, y);    Tnode ans2 = query(Rson, mid+1, R, x, y);    if(ans1.val == ans2.val)  return Tnode(ans1.val, ans1.cnt + ans2.cnt);    else if(ans1.cnt > ans2.cnt)  return Tnode(ans1.val, ans1.cnt - ans2.cnt);    else  return Tnode(ans2.val, ans2.cnt - ans1.cnt);}Treap *NewTnode(){    Node[cur].lson = Node[cur].rson = NULL;    return Node+cur++;}void Treap_L_Rot(Treap *&a){    Treap *b = a->rson;    a->rson = b->lson;    b->lson = a;    a = b;    a->lson->Recount();    a->Recount();}void Treap_R_Rot(Treap *&a){    Treap *b = a->lson;    a->lson = b->rson;    b->rson = a;    a = b;    a->rson->Recount();    a->Recount();}void Treap_Insert(Treap *&p, int val){    if(!p){      p = NewTnode();      p->val = val;      p->fix = rand();      p->size = 1;    }    else if(val <= p->val){      p->size ++;      Treap_Insert(p->lson, val);      if(p->lson->fix < p->fix)  Treap_R_Rot(p);    }    else{      p->size ++;      Treap_Insert(p->rson, val);      if(p->rson->fix < p->fix)  Treap_L_Rot(p);    }}void Treap_Del(Treap *&p, int val){    if(p->val == val){      if(!(p->lson && p->rson)){        if(p->lson)  p = p->lson;        else  p = p->rson;      }      else if(p->lson->fix < p->rson->fix){        Treap_R_Rot(p);        Treap_Del(p->rson, val);        p->size --;      }      else{        Treap_L_Rot(p);        Treap_Del(p->lson, val);        p->size --;      }    }    else if(val < p->val)  Treap_Del(p->lson, val), p->size --;    else  Treap_Del(p->rson, val), p->size --;}int Get(Treap *p, int x){    if(!p)  return 0;    if(p->val <= x)  return p->lsize() + 1 + Get(p->rson, x);    else  return Get(p->lson, x);}int main(){    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i++)  scanf("%d", &org[i]);    build(1, 1, n);    for(int i = 1; i <= n; i++)  Root[i] = NULL;    for(int i = 1; i <= n; i++)  Treap_Insert(Root[org[i]], i);    int l, r, s, k, a;    for(int i = 1; i <= m; i++){      scanf("%d%d%d%d", &l, &r, &s, &k);      person = query(1, 1, n, l, r).val;      if(Get(Root[person], r) - Get(Root[person], l-1) <= (r - l + 1) / 2)  person = s;      printf("%d\n", person);      for(int j = 1; j <= k; j++){        scanf("%d", &a);        update(1, 1, n, a, a, person);        Treap_Del(Root[org[a]], a);        Treap_Insert(Root[org[a] = person], a);      }    }     person = query(1, 1, n, 1, n).val;    if(Get(Root[person], r) - Get(Root[person], l-1) <= n / 2)  person = -1;    printf("%d\n", person);    return 0;}

这里写图片描述

One more time, one more chance.

原创粉丝点击