【BZOJ1568】【Tyvj3490】Blue Mary开公司 李超线段树


Time:2016.08.02
Author:xiaoyimi
转载注明出处谢谢



传送门1
传送门2
思路:
题意大致为
维护有斜率和截距的若干直线,并求直线x=T(T∈N)与当前已加入直线交点的 ymax
利用李超线段树的优势区间维护[1,50000]的直线相交情况
新加入直线与线段树节点所存的原直线进行比较,取具有优势区间的直线作为线段树节点所存的新直线,另一直线在相应的区间内继续下放,最多下放 log(n)
查询时直接比较最大值即可,每次访问 log(n) 个节点
总时间复杂度 nlog(n) (默认天数与询问次数同一数量级,均为n)
(以上均为个人见解,可能有不对的地方,望大神指正,共同进步)
注意:
好像不用标记??应该是有更优的做法吧,我的code码着码着就不像超哥线段树了
BZOJ跑了648ms
Tyvj在1s左右
代码:

#include<cstdio>
#include<iostream>
#define M 100004
#define inf 1e9
using namespace std;
int n;
char ch;
struct Seg
{
    double k,b;
    bool operator ==(const Seg other)const{
        return k==other.k&&b==other.b;
    }
}tr[M<<1];
Seg null={0,-inf};
double cal(int T,Seg x){return x.k*T+x.b;}
Seg judge(int T,Seg x,Seg y){return cal(T,x)>=cal(T,y)?x:y;}
void update(int rt,int begin,int end,Seg node)
{
    Seg f1=judge(begin,tr[rt],node),
        f2=judge(end,tr[rt],node);
    if (f1==tr[rt]&&f2==tr[rt]) return;
    else if (f1==node&&f2==node)
        tr[rt]=node;
    else
    {
        int mid=begin+end>>1;
        double p=(node.b-tr[rt].b)/(tr[rt].k-node.k);
        if (tr[rt].k>=node.k)
            if (p<=mid)
                update(rt<<1,begin,mid,node);
            else
                swap(node,tr[rt]),
                update(rt<<1|1,mid+1,end,node);
        else
            if (p>mid)
                update(rt<<1|1,mid+1,end,node);
            else
                swap(node,tr[rt]),
                update(rt<<1,begin,mid,node);
    }
}
Seg get(int rt,int begin,int end,int T)
{
    if (begin==end) return tr[rt];
    Seg p=tr[rt];
    int mid=begin+end>>1;
    if (mid>=T) return judge(T,p,get(rt<<1,begin,mid,T));
    else return judge(T,p,get(rt<<1|1,mid+1,end,T));
}
main()
{
    scanf("%d",&n);
    while (n--)
    {
        ch=getchar();
        while (ch!='P'&&ch!='Q') ch=getchar();
        while (getchar()!=' ');
        if (ch=='P')
        {
            double k,b;
            scanf("%lf%lf",&b,&k);
            update(1,0,50000-1,(Seg){k,b});
        }
        else
        {
            int p;
            scanf("%d",&p);
            p--;
            Seg ans=get(1,0,50000-1,p);
            printf("%d\n",(int)(cal(p,ans)/100));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值