【权值线段树】Day 7 提高组模拟B组 T2 魔道研究

题目大意

给定一些书和你所能携带的书的总量,问在每次操作后你能获得的最大价值

解题思路

权值线段树查找区间第 k 大进行踢出操作,然后借书相当于区间增值,由于空间很大,所以需要动态开点,要不然会 M L E
注意:数据很大,最好不要用宏定义最小值,要不然会迷之WA

代码

#include<cstdio>
#include<algorithm>
#define N 17000021
#define LL long long
using namespace std;int cnt;
int root[N],le[N],ri[N],t[N],n,m,x,y;
LL s[N],ans,f;char c;
inline LL read()
{
    f=0;
    while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    return f;
}
void write(LL x){if(x>9) write(x/10);putchar(x%10+48);return;}
void add(int &k,int l,int r,int x,int d)//&是为了动态开店节省空间
{
    if(!k) k=++cnt;//动态开点
    if(d) t[k]++,s[k]+=x;else t[k]--,s[k]-=x;//修改
    if(l==r) return;
    int mid=(l+r)>>1;
    if(x<=mid) add(le[k],l,mid,x,d);else add(ri[k],mid+1,r,x,d);
    return;
}
int ask(int k,int l,int r,int x)//查询操作
{
    if(l==r)
    {
        ans+=l*min(x,t[k]);
        return l;
    }
    int mid=(l+r)>>1;
    if(t[ri[k]]>=x) return ask(ri[k],mid+1,r,x);//在右边
    else
    {
        ans+=s[ri[k]];
        return ask(le[k],l,mid,x-t[ri[k]]);//在左边
    }
}
int main()
{
    freopen("grimoire.in","r",stdin);
    freopen("grimoire.out","w",stdout);
    n=read();m=read();
    for (int i=1;i<=m;i++)
    {
        scanf("\n%c",&c);
        if(c=='B')
        {
            x=read();y=read();
            int k=ask(root[x],0,1e9,x);
            add(root[x],0,1e9,y,1);//借书
            if(y>=k)//超过了
            {
                add(root[0],0,1e9,y,1);//y来
                add(root[0],0,1e9,k,0);//k走
            }
        }
        else
        {
            x=read();y=read();
            int k=ask(root[x],0,1e9,x+1);
            add(root[x],0,1e9,y,0);//还书
            if(y>=k)//超过了
            {
                add(root[0],0,1e9,y,0);//y走
                add(root[0],0,1e9,k,1);//k来
            }
        }
        ans=0;
        ask(root[0],0,1e9,n);//查询
        write(ans);putchar(10);//输出
    }
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/81040149