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);
}

                
            
        
浙公网安备 33010602011771号