【JZOJ5620】修炼

Description

这里写图片描述

Solution

考虑每个有买卖的日子,对于一个魂导器的贡献,它其实是一条直线。

那么对于每个日子,加入合法的直线,在线段树上更新即可。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define ll long long
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
using namespace std;
const int N=1e5+10;
const ll inf=1e15;
vector<int> b[N];
struct node{
    int d,p,r,g;
}a[N];
struct line{
    int k;
    ll b;
    ll f(int x){
        return (ll)k*x+b;
    }
}ln[N];
struct tree{
    int l,r,o;
}tr[N*50];
int tot=1,cnt=0;
int n,C,D,m=0;
bool cmp(node x,node y){
    return x.d<y.d;
}
double meet(int x,int y){
    return (ln[y].b-ln[x].b)*1.0/(ln[x].k-ln[y].k);
}
int change(int v,int l,int r,int x){
    if(!v) v=++tot;
    if(!tr[v].o) {tr[v].o=x;return v;}
    int o=tr[v].o;
    double p=meet(x,o);
    int mid=(l+r)>>1;
    if(p<=l*1.0 || p>=r*1.0 || ln[x].k==ln[o].k){
        if(ln[x].f(mid)>ln[o].f(mid)) tr[v].o=x;
        return v;
    }
    if(p<=mid*1.0){
        if(ln[x].k>ln[o].k) tr[v].o=x,tr[v].l=change(tr[v].l,l,mid,o);
        else tr[v].l=change(tr[v].l,l,mid,x);
    }
    else{
        if(ln[x].k<ln[o].k) tr[v].o=x,tr[v].r=change(tr[v].r,mid+1,r,o);
        else tr[v].r=change(tr[v].r,mid+1,r,x);
    }
    return v;
}
ll find(int v,int l,int r,int x){
    if(!v) return -inf;
    int o=tr[v].o;
    if(!o) return -inf;
    ll tmp=ln[o].f(x);
    if(l==r) return tmp;
    int mid=(l+r)/2;
    if(x<=mid) return max(tmp,find(tr[v].l,l,mid,x));
    else return max(tmp,find(tr[v].r,mid+1,r,x));
}
void add(int k,ll b){
    ln[++cnt].k=k,ln[cnt].b=b;
    change(1,1,D+1,cnt);
}
int d[N];
int main()
{
    freopen("practice.in","r",stdin);
    freopen("practice.out","w",stdout);
    int num,Q;
    scanf("%d %d",&num,&Q);
    while(Q--){
        scanf("%d %d %d",&n,&C,&D);
        fo(i,1,n) scanf("%d %d %d %d",&a[i].d,&a[i].p,&a[i].r,&a[i].g);
        sort(a+1,a+n+1,cmp);
        m=0;
        fo(i,1,n){
            if(a[i].d>D) break;
            a[i].d!=a[i-1].d?d[++m]=a[i].d:m;
            b[m].push_back(i);
        }
        cnt=0,add(0,C);
        fo(i,1,m){
            int o=b[i].size();
            ll now=find(1,1,D+1,d[i]);
            fo(j,0,o-1){
                int p=b[i][j];
                if(a[p].p<=now) add(a[p].g,-1ll*a[p].g*d[i]+now-a[p].p+a[p].r-a[p].g);
            }
        }
        ll ans=find(1,1,D+1,D+1);
        printf("%lld\n",ans);
        mem(tr),mem(ln),mem(d),tot=1;
        fo(i,1,m) b[i].clear();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值