BZOJ 1568 [JSOI2008]Blue Mary开公司

BZOJ 1568 [JSOI2008]Blue Mary开公司

线段树

题意

插入n条直线 y=kx+b ,然后查询某x位置所有直线中的最大值。

思路

线段树,每个区间维护:在这个区间内,值最大的线段的k与b。有个性质:因为所有斜率非负,所以斜率是不减的。就是越往后的区间,斜率不会小。

每次加入一条直线,先判断直线的斜率和本区间记录的直线的斜率,再判断区间中点哪条直线的答案大,如果是斜率大的直线答案大,因为斜率递增,所以在[mid+1,r]中斜率小的直线不会再产生贡献,那么我们把当前区间的答案更改为斜率大的直线,同时将斜率小的直线向[l,mid]中下方。

如果是斜率小的直线的答案大,那么斜率大的直线只可能在[mid+1,r]中产生贡献,那么就把当前区间的答案更改为斜率较小的直线,并把斜率大的直线向[mid+1,r]下方。

查询答案的时候就把经过的区间所记录的直线该点的值都计算一下,取最大值就是答案。

标记永久化,带待我看懂再说。%%%

代码

我根据自己的理解,优化了一下边界,跑的可能更快了一点。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=50007;

struct Stree
{
    double k;double b;
}stree[MAXN<<2];

void build()
{
    M(stree, 0);
}

void update(double K, double B, int l, int r, int rt)
{
    if(l==r) return;
    double l1=(double)K*l+B, r1=(double)K*r+B;
    double l2=(double)stree[rt].k*l+stree[rt].b,
        r2=(double)stree[rt].k*r+stree[rt].b;
    double x=(B-stree[rt].b)/(stree[rt].k-K);
    int mid=(l+r)>>1;

    if((l1>=l2&&r1>r2)||(l1>l2&&r1>=r2))
        stree[rt].k=K, stree[rt].b=B;
    else if(l1<=l2&&r1<=r2) return;
    else
    {
        if(x<=mid)
        {
            if(l1>l2)
                update(K, B, lson);
            else
            {
                update(stree[rt].k, stree[rt].b, lson);
                stree[rt].k=K, stree[rt].b=B;
            }
        }
        else
        {
            if(l1>l2)
            {
                update(stree[rt].k, stree[rt].b, rson);
                stree[rt].k=K, stree[rt].b=B;
            }
            else
                update(K, B, rson);
        }
    }
}
double query(int pos, int l, int r, int rt)
{
    double k=stree[rt].k, b=stree[rt].b;
    double res=k*pos+b;
    if(l==r) return res;
    int mid=(l+r)>>1;
    if(pos<=mid)
        res=max(res, query(pos, lson));
    else
        res=max(res, query(pos, rson));
    return res;
}
int main()
{/*
    freopen("in.txt", "r", stdin);
    freopen("ou1.txt", "w", stdout);*/
    build();
    int n;scanf("%d", &n);
    char s[10];double k, b;int pos;
    while(n--)
    {
        scanf("%s", s);
        if(s[0]=='P')
        {
            scanf("%lf%lf", &b, &k);
            update(k, b-k, 1, MAXN, 1);
        }
        else
        {
            scanf("%d", &pos);
            double res=query(pos, 1, MAXN, 1);
            double tmp=res/100.0;
            int R=tmp;
            printf("%d\n", R);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值