JZOJ5620. 【NOI2018模拟4.1】修炼

题目

这里写图片描述
这里写图片描述

题解

很自然的想法,首先一定会按照 d i 排序。
f i 表示前i个物品,第i个物品一定购买的时候最大的魂力。
转移的时候,就枚举上一个购买的物品j,
计算这一段时间j物品的收益。
f i = f j + ( d i d j 1 ) g j + r j
这样子是 O ( n 2 ) 的。

整理一下式子,就变成了:
f i = d i g j + ( f j ( d j 1 ) g j + r j )
可以看出,对于每一个不同的i,相同的j的影响是一个一次函数,
也就是一条直线,斜率为 g j
由于 g i 并没有单调性,所以就不能用单调队列或者栈来维护。

用线段树来维护。
线段树的一个区间,表示一条线段,
如果新插入的线段完全大于原有的线段,
就整个区间直接用新的线段去替换。
如果新旧线段有交点,
那么就分别递归到下面一层。
查询的时候,就将包含这个点的每个区间的线段取个max就可以了。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(ll &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

struct node
{
    ll d,p,r,g;
}a[N];

struct arr
{
    ll k,b;
    int l,r;
}tr[N*50],w;

bool cmp(node a,node b)
{
    return a.d<b.d;
}

ll n,f[N],q,m,c,d,tot,t;

void work()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=i-1;j>=0;j--)
        {
            t=f[j]+(a[i].d-a[j].d-1)*a[j].g+a[j].r;
            if(t>=a[i].p)f[i]=max(f[i],t-a[i].p);
        }
    }
}

void ins(int x,ll l,ll r)
{
    ll t1=w.k*l+w.b,t2=w.k*r+w.b;
    ll w1=tr[x].k*l+tr[x].b,w2=tr[x].k*r+tr[x].b;
    if((t1>=w1 && t2>=w2))
    {
        tr[x].k=w.k;
        tr[x].b=w.b;
        return;
    }
    if(l==r || (t1<w1 && t2<w2))return;
    ll m=(l+r)>>1;
    if(!tr[x].l)tr[x].l=++tot,tr[tot].b=tr[tot].k=tr[tot].l=tr[tot].r=0;
    if(!tr[x].r)tr[x].r=++tot,tr[tot].b=tr[tot].k=tr[tot].l=tr[tot].r=0;
    ins(tr[x].l,l,m);
    ins(tr[x].r,m+1,r);
}

ll find(int x,ll l,ll r,ll pos)
{
    if(x==0)return 0;
    if(l==r)return pos*tr[x].k+tr[x].b;
    ll m=(l+r)>>1;
    if(pos<=m)return max(pos*tr[x].k+tr[x].b,find(tr[x].l,l,m,pos));
        else return max(pos*tr[x].k+tr[x].b,find(tr[x].r,m+1,r,pos));
}

int main()
{
    freopen("practice.in","r",stdin);
    freopen("practice.out","w",stdout);

    for(read(m),read(q);q;q--)
    {
        memset(f,128,sizeof(f));
        read(n);read(c);read(d);
        for(int i=1;i<=n;i++)
            read(a[i].d),read(a[i].p),read(a[i].r),read(a[i].g);
        d++;n++;a[n].d=d;a[n].p=a[n].g=a[n].r=0;
        sort(a+1,a+1+n,cmp);
        tot=1;tr[tot].b=c;tr[tot].k=tr[tot].l=tr[tot].r=0;
        for(int i=1;i<=n;i++)
        {
            t=find(1,1,d,a[i].d);
            if(t<a[i].p)continue;
            f[i]=t-a[i].p;
            w.k=a[i].g;w.b=f[i]+a[i].r-(a[i].d+1)*a[i].g;
            ins(1,1,d);
        }
        write(f[n]);P('\n');
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/lijf2001/article/details/79888512