CF-557C Arthur and Table 权值线段树

Arthur and Table

题意

一个桌子有n个腿,每个腿都有一个高度,当且仅当最高的腿的数量大于桌子腿数量的一半时,桌子才是稳定的。特殊的是当只有一个腿时,桌子是稳定的,当有两个腿时两个腿必须都得是最高的,才稳定。

分析

这题其实和去年的牛客的一道砍树题一样的(但是我没想起来那题,当时看的题解,稍微改了一下代码,就交了印象不深刻。。。)

Governing sand

首先我们按照桌子腿的高度从小到大排序,然后枚举当前最高的高度。

大于当前高度的肯定都要删除掉,用一个suf后缀来维护。

我们知道当前高度的桌腿的数量,根据这个数量算出小于这个高度的我们要删除几个?x个

然后我们用一颗权值线段树维护桌腿的数量以及价值。

查询前x个最小的权值和,当前高度的花费就是suf[i+1]+query(x)。

代码

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const double eps=1e-14;

struct note
{
    int wei,val;
}arr[N];
bool cmp(note a,note b)
{
    return a.wei<b.wei;
}
int suf[N];
struct tree
{
    int num,sum;
}node[N];
void build(int rt,int l,int r)
{
    node[rt].num=node[rt].sum=0;
    if(l==r) return ;
    int mid=(l+r)/2;
    build(rt*2,l,mid);
    build(rt*2+1,mid+1,r);
}
void pushup(int rt)
{
    node[rt].num=node[rt*2].num+node[rt*2+1].num;
    node[rt].sum=node[rt*2].sum+node[rt*2+1].sum;
}
void update(int rt,int l,int r,int pos)
{
    if(l==r)
    {
        node[rt].num++;
        node[rt].sum+=l;
        return;
    }
    int mid=(l+r)/2;
    if(pos<=mid) update(rt*2,l,mid,pos);
    else update(rt*2+1,mid+1,r,pos);
    pushup(rt);
}
int query(int rt,int l,int r,int k)
{
    if(l==r) return k*l;
    int lnum=node[rt*2].num;//左子树中桌腿数量
    int mid=(l+r)/2;
    if(k<=lnum) return query(rt*2,l,mid,k);
    else return node[rt*2].sum+query(rt*2+1,mid+1,r,k-lnum);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&arr[i].wei);
    for(int i=1;i<=n;i++)
        scanf("%d",&arr[i].val);
    sort(arr+1,arr+1+n,cmp);//按照高度从小到大排序
    for(int i=n;i;i--)//计算后缀
        suf[i]=suf[i+1]+arr[i].val;
    int now,ans=inf;
    build(1,1,200);
    for(int i=1;i<=n;i++)
    {
        now=0;
        int j=i;
        while(j<=n&&arr[j].wei==arr[i].wei)//计算当前高度的桌腿的数量,下标
        {
            j++;
            now++;
        }
        int x=max(i-now,0);//小于当前高度,要删去的数量
        ans=min(ans,suf[j]+query(1,1,200,x));//计算贡献
        for(int k=i;k<j;k++)//把当前高度更新如线段树
            update(1,1,200,arr[k].val);
        i=j-1;
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2020-04-25 11:40  Valk3  阅读(148)  评论(0编辑  收藏  举报