CF 458C - Elections (枚举 + 线段树)

题意:有n个人,每个人都有一个选票,第i个人想要选编号为ai的人,如果想贿赂这个人让他选自己,就需要花费bi,问想让自己获胜(得票最多)的最小花费是多少。

思路:枚举一下最终的得票数,那么对于比当前枚举的票数多的人,那么多出的那部分一定要买走,剩下的,就是在剩下的人中,选择花费最少的人让自己得到相应的票数。从大到小枚举票数,假设之前枚举到k,那么枚举k-1时,在k要收买多出的那部分在k-1的时候也要收买,因此,这部分人会不断减少。将花费从小到大排序以后,插入线段树,线段树维护区间和还有区间中还没被收买的人的数量。那么,如果枚举到k,此时一定要收买的人有p个,那么就要在线段树中找前k-p个人就好了。


代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
struct Node
{
    int id,a,b;
    Node(int id=0,int a=0,int b=0):id(id),a(a),b(b){}
    bool operator < (const Node &a) const
    {
        return b < a.b;
    }
}node[maxn];
vectorvt[maxn];
int rank[maxn],used[maxn];
bool cmp(int a,int b)
{
    return vt[a].size() > vt[b].size();
}
int cnt[maxn<<2],sum[maxn<<2];
void PushUp(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1];
}
void build(int l,int r,int rt)
{
    if(l == r)
    {
        cnt[rt] = 1;
        sum[rt] = node[l].b;
        return ;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    PushUp(rt);
}
void Update(int p,int l,int r,int rt)
{
    if(l == r)
    {
        cnt[rt] = sum[rt] =0;
        return ;
    }
    int m =(l+r)>>1;
    if(m >= p) Update(p,l,m,rt<<1);
    else Update(p,m+1,r,rt<<1|1);
    PushUp(rt);
}
int Query(int k,int l,int r,int rt)
{
    if(l == r) return sum[rt];
    int m=(l+r)>>1;
    if(cnt[rt<<1] >= k) return Query(k,l,m,rt<<1);
    else return sum[rt<<1] + Query(k-cnt[rt<<1],m+1,r,rt<<1|1);
}
int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w ",stdout);
    int n;
    scanf("%d",&n);
    for(int i =1 ;i <= n;++i)
        scanf("%d%d",&node[i].a,&node[i].b);
    sort(node + 1,node + n +1);
    for(int i =1 ;i <= n;++i)
        node[i].id = i;
    for(int i =1;i <= n;++i)
    {
        vt[node[i].a].push_back(node[i]);
    }
    for(int i = 0;i < maxn ;++i)
        rank[i] = i;
    sort(rank,rank + maxn -1,cmp);
    build(1,n,1);
    int ans = inf,last = 0,has = 0,tmp;
    Node nd;
    for(int i = n;i >= 1;--i)
    {
        for(int j = 0;j < maxn;++j)
        {
            int x = rank[j];
            if((int)vt[x].size() - used[x] >= i)
            {
                nd = vt[x][used[x]];
                last += nd.b;
                Update(nd.id,1,n,1);
                used[x]++;
                has++;
            }
            else break;
        }
        tmp = last;
        if(has < i)
        {
            tmp += Query(i-has,1,n,1);
        }
        ans = min(ans,tmp);
    }
    printf("%d\n",ans);
    return 0;
}


你可能感兴趣的:(数据结构,线段树)