[BZOJ 1568][JSOI2008]Blue Mary開公司

[BZOJ 1568][JSOI2008]Blue Mary開公司

題意

\(n\) 次操做, 維護一個一次函數集合 \(S\). 有兩種操做:php

  • 給定 \(b\)\(k\), 向 \(S\) 中插入一個函數 \(f(x)=kx+b\).
  • 給定一個 \(x\), 求 \(\max\limits_{f\in S}\{f(x)\}\).

\(n\le 1\times 10^5,x\le 50000\).c++

題解

AFO前的打板子平常函數

講道理爲啥一個 \(\log\) 的題數據範圍纔給這麼點啊this

李超線段樹板子題.spa

關於李超樹

李超線段樹是一種特殊的標記永久化線段樹. 說它特殊, 是由於標記雖然永久化可是依然會有下傳.code

在這種線段樹中, 每一個結點維護的是覆蓋當前結點的 "優點線段", 也即當前結點中有超過一半的 \(x\) 的查詢值在這個線段處取到. 因爲是一次函數, 實際上等價於當前結點的 \(mid\) 處以這個函數爲最大函數值.blog

當插入一個函數 \(f(x)\) 的時候, 分三種狀況:遞歸

  • 若是原來的優點線段 \(g(x)\) 在當前結點所在區間的全部 \(x\) 處都有 \(g(x)\le f(x)\), 那麼直接把當前結點的優點線段設爲 \(f(x)\) 並結束插入過程.
  • 若是 \(\forall x,f(x)\le g(x)\), 那麼不進行任何修改, 結束插入過程.
  • 若是不知足上面兩個條件, 那麼判斷 \(g(x)\)\(f(x)\) 哪一個是優點線段, 並計算出它們的交點. 顯然不包含交點的一半子樹中 \(f(x)\)\(g(x)\) 的關係如上面兩種狀況, 可是被淘汰的線段仍然可能在另外一個子樹中的某個點中成爲優點線段, 因此將被淘汰的線段遞歸處理.

查詢的時候就像普通的標記永久化線段樹同樣查到葉子並用路徑上的結點上存儲的優點線段更新答案.get

不難發現若是維護的是函數集合的話複雜度是一個 \(\log\) 的. 若是是在某一段特定區間上產生一個一次函數狀的貢獻的話, 會先將這個函數分佈到 \(O(\log n)\) 個結點上而後下傳, 這種狀況下時間複雜度是 \(O(\log^2 n)\) 的.it

李超樹的實際表現仍是很優秀的, 不少時候在區間維護的狀況下也不會比普通線段樹慢太多.

具體實現的時候善用 std::swap 能夠幫助減小冗餘代碼. 並且交點不用真的去求, 由於是一次函數因此判斷區間兩端的函數值的大小關係就知道它們是否在這個區間內相交了.

關於這道題

這道坑爹的板子題有幾個小坑點:

  • 輸出的時候須要轉單位.
  • 初值爲 \(0\) (也就是若是惟一的函數在查詢的 \(x\) 處爲負數的時候應該查得 \(0\)).
  • 由於初值是 \(0\) 因此不存在向 \(0\) 取整仍是向下取整的問題.

參考代碼

#include <bits/stdc++.h>

const int MAXN=1e5+10;

struct Line{
    double k;
    double b;
    Line(double a,double b):k(a),b(b){}
    double operator()(const double& x)const{
        return k*(x-1)+b;
    }
};

struct Node{
    int l;
    int r;
    Line f;
    Node* lch;
    Node* rch;
    Node(int,int);
    double Query(int);
    void Insert(Line);
};

char buf[100];

int main(){
    int q;
    bool flag=false;
    scanf("%d",&q);
    Node* N=new Node(1,5e4);
    while(q--){
        scanf("%s",buf);
        if(*buf=='P'){
            flag=true;
            double k,b;
            scanf("%lf%lf",&b,&k);
            N->Insert(Line(k,b));
        }
        else{
            int x;
            scanf("%d",&x);
            if(!flag)
                puts("0");
            else
                printf("%d\n",int(N->Query(x)/100));
        }
    }
    return 0;
}

double Node::Query(int x){
    if(this->l==this->r)
        return this->f(x);
    else{
        if(x<=this->lch->r)
            return std::max(this->f(x),this->lch->Query(x));
        else
            return std::max(this->f(x),this->rch->Query(x));
    }
}

void Node::Insert(Line f){
    int mid=(this->l+this->r)>>1;
    if(f(mid)>this->f(mid))
        std::swap(f,this->f);
    double ld=this->f(this->l)-f(this->l);
    double rd=this->f(this->r)-f(this->r);
    if(ld>=0&&rd>=0)
        return;
    else if(rd>=0)
        this->lch->Insert(f);
    else
        this->rch->Insert(f);
}

Node::Node(int l,int r):l(l),r(r),f(0,0){
    if(l!=r){
        int mid=(l+r)>>1;
        this->lch=new Node(l,mid);
        this->rch=new Node(mid+1,r);
    }
}

相關文章
相關標籤/搜索