1. 程式人生 > >BZOJ.4515.[SDOI2016]遊戲(樹鏈剖分 李超線段樹)

BZOJ.4515.[SDOI2016]遊戲(樹鏈剖分 李超線段樹)

BZOJ
洛谷

每次在路徑上加的數是個一次函式,容易看出是樹剖+李超線段樹維護函式最小值。所以其實依舊是模板題。
橫座標自然是取個確定的距離標準。取每個點到根節點的距離\(dis[i]\)作為\(i\)的橫座標好了,這樣對於同一條重鏈,橫座標還是遞增的。

\(w=LCA(u,v)\)。如果在\((u,v)\)路徑上加入直線\(y=kx+b\)
對於在\(u\to w\)路徑上的點,每個點\(i\)的橫座標就是\(dis_u-dis_i\),所以對於\(i\)\(y=k(dis_u-dis_i)+b=-k\cdot dis_i+k\cdot dis_u+b\),依舊是原座標系一條\(k=-k,\quad b=k\cdot dis_u+b\)

的直線。所以直接樹剖+線段樹維護即可。
另一條\(w\to v\)的路徑同理。

線段樹的每個節點維護它以及它兒子中的最小值\(mn[rt]\)(每個節點維護的都是一條直線,所以最小值顯然就在兩端點中取)。
區間查詢的時候,如果當前區間完全包含於詢問區間,就直接返回\(mn[rt]\);否則答案與當前點維護的線段兩端點的函式值取個\(\min\),繼續遞迴即可。

這樣區間修改+樹剖的複雜度是\(O(n\log^3n)\),你只要相信樹剖+李超線段樹的常數很小就好了。。
一個可能的解釋

一.因為一個線段的交點正好在區間的左數第二個點和右數第二個點的機率特別小,所以每次二分不一定要到末尾才結束,所以一般可以把交點平均在二分中間時刻停止,因此這裡有個二分之一的常數;二.線段樹不一定是最壞情況(每層都有兩個點),所以這裡有個約3/4的常數,又由於樹鏈剖分不一定是最壞情況,所以這裡又有個約1/2的常數,多虧了出題人良心),所以這題O(nlog^3n)是可以通過的。

注意線段樹裡的下標都是\(dis[ref[i]]\)

寫的時候的錯誤:

  1. Modify中的m和mx別混用。。
  2. 就算是第一次覆蓋區間也要與mn[rt]取min。
  3. 各種細節。
//35240kb   9032ms
#include <cstdio>
#include <cctype>
#include <assert.h>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5;
const LL INF=123456789123456789ll;

int n,H[N],Enum,nxt[N<<1],to[N<<1],len[N<<1],fa[N],dep[N],sz[N],son[N],top[N],dfn[N],ref[N];
LL dis[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson l,m,ls
    #define rson m+1,r,rs
    #define S N<<2
//  int have[S];
    LL K[S],B[S],mn[S],Lx[S],Rx[S],Mx[S];
    #undef S
    #define Update(rt) mn[rt]=std::min(mn[rt],std::min(mn[ls],mn[rs]))
//  void Init(int n)
//  {
//      for(int i=n<<2; i; --i) mn[i]=INF;
//  }
    void Build(int l,int r,int rt)
    {
        B[rt]=mn[rt]=INF, Lx[rt]=dis[ref[l]], Rx[rt]=dis[ref[r]];
        if(l==r) return;
        int m=l+r>>1; Mx[rt]=dis[ref[m]], Build(lson), Build(rson);
    }
    void Modify(int l,int r,int rt,int L,int R,LL k,LL b)
    {
        if(L<=l && r<=R)
        {
            LL lx=Lx[rt],rx=Rx[rt],l0=K[rt]*lx+B[rt],r0=K[rt]*rx+B[rt],l1=k*lx+b,r1=k*rx+b;
//          if(!have[rt]) {have[rt]=1, K[rt]=k, B[rt]=b, mn[rt]=std::min(mn[rt],std::min(l1,r1)); return;}//當然要和當前節點取min啊!(由另一部分割槽間PushUp來的啊)
            if(l0<=l1 && r0<=r1) return;
            if(l1<=l0 && r1<=r0) {K[rt]=k, B[rt]=b, mn[rt]=std::min(mn[rt],std::min(l1,r1)); return;}//更新mn!
            int m=l+r>>1;//m不是mx。。
            LL mx=Mx[rt]; double p=1.0*(B[rt]-b)/(k-K[rt]);
            if(l0<l1)
                if(p<=(double)mx) Modify(lson,L,R,K[rt],B[rt]), K[rt]=k, B[rt]=b;
                else Modify(rson,L,R,k,b);
            else
                if(p<=(double)mx) Modify(lson,L,R,k,b);
                else Modify(rson,L,R,K[rt],B[rt]), K[rt]=k, B[rt]=b;
            mn[rt]=std::min(mn[rt],std::min(l1,r1)), Update(rt);//!
            return;
        }
        int m=l+r>>1;
        if(L<=m) Modify(lson,L,R,k,b);
        if(m<R) Modify(rson,L,R,k,b);
        Update(rt);
    }
    LL Query(int l,int r,int rt,int L,int R)
    {
        if(L<=l && r<=R) return mn[rt];
        LL res=INF;
        if(B[rt]!=INF)
        {
            LL lx=std::max(l,L),rx=std::min(r,R);
            res=std::min(K[rt]*dis[ref[lx]],K[rt]*dis[ref[rx]])+B[rt];
        }
        int m=l+r>>1;
        if(L<=m) res=std::min(res,Query(lson,L,R));
        if(m<R) res=std::min(res,Query(rson,L,R));
        return res;
    }
}T;

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now*f;
}
inline void AE(int w,int u,int v)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;
}
inline int LCA(int u,int v)
{
    while(top[u]!=top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
    return dep[u]>dep[v]?v:u;
}
void DFS1(int x)
{
    int mx=0; sz[x]=1;
    for(int i=H[x],v; i; i=nxt[i])
        if((v=to[i])!=fa[x])
            fa[v]=x, dep[v]=dep[x]+1, dis[v]=dis[x]+len[i], DFS1(v), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v],son[x]=v);
}
void DFS2(int x,int tp)
{
    static int Index=0;
    top[x]=tp, ref[dfn[x]=++Index]=x;
    if(son[x])
    {
        DFS2(son[x],tp);
        for(int i=H[x],v; i; i=nxt[i])
            if((v=to[i])!=fa[x]&&v!=son[x]) DFS2(v,v);
    }
}
void Modify(int u,int w,LL k,LL b)
{
    while(top[u]!=top[w])
        T.Modify(1,n,1,dfn[top[u]],dfn[u],k,b), u=fa[top[u]];
    T.Modify(1,n,1,dfn[w],dfn[u],k,b);
}
LL Query(int u,int v)
{
    LL res=INF;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
        res=std::min(res,T.Query(1,n,1,dfn[top[u]],dfn[u])), u=fa[top[u]];
    }
    if(dep[u]>dep[v]) std::swap(u,v);
    return std::min(res,T.Query(1,n,1,dfn[u],dfn[v]));
}

int main()
{
    n=read(); int m=read();
    for(int i=1; i<n; ++i) AE(read(),read(),read());
    DFS1(1), DFS2(1,1), T.Build(1,n,1);
    for(int i=1; i<=m; ++i)
        switch(read())
        {
            case 1:
            {
                int u=read(),v=read(),w=LCA(u,v),k=read(),b=read();
                Modify(u,w,-k,dis[u]*k+b), Modify(v,w,k,(dis[u]-(dis[w]<<1))*k+b);
                break;
            }
            case 2: printf("%lld\n",Query(read(),read())); break;
        }

    return 0;
}