51nod 1494 选举拉票【贪心】【扫描线】【线段树】

Description

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

题解

直接做很困难,所以我们想到了枚举我的选票数,所有选票数比我多的人的选票都要被我抢到小于我的选票数(一定是从小到大抢),这样抢来以后,不一定选票数达到了我枚举的选票数,所以我就需要从所有剩下的选票中选出一些来补上,这样就变成了求全局的前k小(可以直接用线段树),枚举的时候就是扫描线从大到小枚举,所有选票数比我多的人的选票中被我选出来的都要从线段树中删除。

代码

#include
#include
#include
#include
#define maxn 100006
#define maxm 10006
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
struct data{
    int l,r,num,sum;
}tree[maxm*4];
int n,m,top,ans,a[maxn],stack[maxn];
bool vis[maxn];
priority_queue<int,vector<int> ,greater<int> > heap[maxn];
void build(int p,int l,int r){
    tree[p].l=l;tree[p].r=r;
    if(l>=r)return;
    int mid=(l+r)>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
void update(int p,int t,int k){
    if(tree[p].l>t||tree[p].rreturn;
    if(tree[p].l==tree[p].r){
        tree[p].num+=k;tree[p].sum+=k*t;return;
    }
    update(p<<1,t,k);update(p<<1|1,t,k);
    tree[p].num=tree[p<<1].num+tree[p<<1|1].num;
    tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
}
int query(int p,int k){
    if(k<=0)return 0;
    if(k>tree[p].num)return 2e9;
    if(tree[p].l==tree[p].r)return tree[p].l*k;
    if(k<=tree[p<<1].num)return query(p<<1,k);
    return tree[p<<1].sum+query(p<<1|1,k-tree[p<<1].num);
}
bool cmp(int x,int y){return heap[x].size()>heap[y].size();}
int main(){
    freopen("nineteen.in","r",stdin);
    freopen("nineteen.out","w",stdout);
    n=_read();
    build(1,0,10000);
    memset(vis,1,sizeof(vis));
    for(int i=1,x,y;i<=n;i++){
        x=_read(),y=_read(),heap[x].push(y);
        if(x&&vis[x])a[++m]=x,vis[x]=0;
        if(x)update(1,y,1);
    }
    sort(a+1,a+1+m,cmp);
    ans=2e9;int s=heap[0].size(),sum=0,num=0;
    for(int i=n;i>=max(1,s);i--){
        for(int j=1;j<=m;j++){
            if(heap[a[j]].size()break;
            while(heap[a[j]].size()>=i)sum+=heap[a[j]].top(),update(1,heap[a[j]].top(),-1),heap[a[j]].pop(),num++;
        }
        ans=min(ans,sum+query(1,i-num-s));
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(线段树,贪心,51nod,扫描线)