【51Nod1494】选举拉票

现在你要竞选一个县的县长。你去对每一个选民进行了调查。你已经知道每一个人要选的人是谁,以及要花多少钱才能让这个人选你。现在你想要花最少的钱使得你当上县长。你当选的条件是你的票数比任何一个其它候选人的多(严格的多,不能和他们中最多的相等)。请计算一下最少要花多少钱。

Input
单组测试数据。
第一行有一个整数n (1 ≤ n ≤ 10^5),表示这个县的选民数目。
接下来有n行,每一行有两个整数ai 和 bi (0 ≤ ai ≤ 10^5; 0 ≤ bi ≤ 10^4),表示第i个选民选的是第ai号候选人,想要让他选择自己就要花bi的钱。你是0号候选人(所以,如果一个选民选你的话ai就是0,这个时候bi也肯定是0)。
Output
输出一个整数表示花费的最少的钱。
Input示例
5
1 2
1 2
1 2
2 1
0 0
Output示例
3

题解
逆向思维,考虑每个人都投自己,删去钱最多的票。
枚举其他候选人的得票数,显然贪心选取每个人代价最大的票,把这些票加入线段树,假设还需要k票,就从线段树中找最小的k个。

代码

#include<bits/stdc++.h>
#define mod 998244353
#define ll long long 
#define inf 0x7fffffff
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int NUM[40005],SUM[40005];
int n,N,mx,M,ans;
vector<int>f[100005];
vector<int>F[100005];
void update(int k,int l,int r,int x)
{
    NUM[k]++;SUM[k]+=x;
    if (l==r)return;
    int mid=(l+r)>>1;
    if (x<=mid) update(k<<1,l,mid,x);
    else update(k<<1|1,mid+1,r,x);
}
int query(int k,int l,int r,int x)
{
    if (l==r) return x*l;
    if (NUM[k]==x) return SUM[k];
    int mid=(l+r)>>1;
    if (NUM[k<<1]>=x) return query(k<<1,l,mid,x);
    else return SUM[k<<1]+query(k<<1|1,mid+1,r,x-NUM[k<<1]);
}
int main()
{
    n=read();
    for (int i=1;i<=n;i++)
    {
        int a=read(),b=read();
        if (b==0) continue;
        ans+=b;
        N=max(a,N);
        mx=max(mx,b);
        f[a].push_back(b);
    }
    for (int i=0;i<=N;i++)
    {
        if (!f[i].size()) continue;
        sort(f[i].begin(),f[i].end(),greater<int>());
        M=max((int)f[i].size(),M);
        for (int j=0;j<f[i].size();j++)
            F[j].push_back(f[i][j]);
    }
    int sum=ans;
    for (int i=0;i<M;i++)
    {
        n-=F[i].size();
        for (int j=0;j<F[i].size();j++)
        {
            update(1,1,mx,F[i][j]);
            sum-=F[i][j];
        }
        int tmp=0;
        if (n<=i+1) tmp=query(1,1,mx,i+2-n);
        ans=min(ans,sum+tmp);
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值