Loading

51Nod-1494 选举拉票 权值线段树

51Nod-1494 选举拉票 权值线段树

题意

现要竞选市长。有\(n\)个选民投票。

每行两个数字\(a_i,b_i\)表示第\(i\)个选民投给\(a_i\)号候选人,必须花费\(b_i\)使他投你的票。

你是第0号候选人。

问最少花多少钱使你竞选成功。

\[1\leq n \leq 10^5\\ 0\leq a_i \leq 10^5,0\leq b_i \leq 10^4 \]

分析

\(n - st到max(1,st)\)枚举要收买多少人。

假设对每个候选人票数小于\(i\)要收买\(num\)个人。那么还需要\(i - num\)个人。

显然我们贪心的选择剩下的人。

为了快速计算这些人的总和,可以建立一颗权值线段树,做到单次查询\(O(logn)\)

权值线段树的节点下标(不是真正的下标)表示权值,num表示个数,sum表示总和(方便询问)

还要注意这里如果size小于i就直接break,大大减少了循环次数。这也是之前排序的原因。

代码

priority_queue<int, vector<int>, greater<int> > q[maxn];
vector<int> id;
int vis[maxn];
int cnt;
int st;

bool cmp(int x, int y) {
    return q[x].size() > q[y].size();
}

struct Node {
    int num, sum;
};

Node node[maxn << 2];

void push_up(int i) {
    node[i].num = node[i << 1].num + node[i << 1 | 1].num;
    node[i].sum = node[i << 1].sum + node[i << 1 | 1].sum;
}

void update(int i, int l,int r,int p, ll v) {
    if (l == r) {
        node[i].num += v;
        node[i].sum += l * v;
        return;
    }
    int mid = l + r >> 1;
    if (p <= mid) update(i << 1, l, mid, p, v);
    else update(i << 1 | 1, mid + 1, r, p, v);
    push_up(i);
}

int query(int i, int l, int r, int k) {
    if (k <= 0) return 0;
    if (node[1].num <  k) return 2e9;
    if (l == r) return l * k;
    int mid = l + r >> 1;
    if (k <= node[i << 1].num) return query(i << 1, l, mid, k);
    else return node[i << 1].sum + query(i << 1 | 1, mid + 1, r, k - node[i << 1].num);
}



int main() {
    int n = readint();
    for (re int i = 0; i < n; i++) {
        int x, y;
        x = readint();
        y = readint();
        if (!x) {
            st++;
            continue;
        }
        q[x].push(y);
        update(1, 0, 10000, y, 1);
        if (!vis[x]) {
            vis[x] = 1;
            id.push_back(x);
        }
    }
    sort(id.begin(), id.end(),cmp);
    int ans = INF;
    int res = 0, num = 0;
    int mx = max(1, st);
    for (re int i = n; i >= mx; i--) {
        for (re int j = 0; j < id.size(); j++) {
            int xx = id[j];
            if (q[xx].size() < i) break;
            while (q[xx].size() >= i) {
                int yy = q[xx].top();
                res += yy;
                update(1, 0, 10000,yy, -1);
                q[xx].pop();
                num++;
            }
        }
        ans = min(ans, res + query(1, 0, 10000, i - num - st));
    }
    Put(ans);
}
posted @ 2020-10-06 14:56  MQFLLY  阅读(134)  评论(0编辑  收藏  举报