【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

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

传送门
思路:
李超线段树
一开始听faebdc讲,并没有听的很懂ww
后来找到良心博文啊有木有
折越

  首先可以把修改转换一下,因为那个dis非常不爽。显然s~t的路径有s~lca和lca~t组成。令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s]),lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x]+(b+a*(d[s]-d[x]*2)),这样就可以得到新的a'和b',然后一个点的值就相当于a'*d[x]+b'了,这样就只和a',b'和d[x]有关了。

   注意到在树链剖分对应的线段树区间中,相连的一部分它们在树种也是相连的,这样就满足d[]单调递增,观察a'*d[x]+b',发现这相当于直线f(x)=a'x+b在d[x](两个x不是一个意思)处的取值!那么就相当于用线段树维护一个区间中的若干条直线在每个部分的最小值。

   不妨考虑标记永久化(这里只是一定程度上的永久化但还是要下传的)。让线段树中的一个节点只对应一条直线,那么如果在这个区间加入一条直线怎么办呢?要分类讨论,设新加入的f1(x)=k1x+b1,原来的f2(x)=k2x+b2,左端点为l,右端点为r,那么有:

   1.f1(d[l])<f2(d[l])且f1(d[r])<f2(d[r]),对应一条直线在两个端点都比另一条小,那么显然在l~r中f1(x)处处比f2(x)小,直接把f2(x)替换为f1(x);

   2.同理若上式的两个符号都为>,那么f1(x)处处不如f2(x)优,不做更改。

   3.k1<k2,那么由于不满足1.2,显然两条直线有交点,此时解不等式f1(x)<f2(x)得到x>(b1-b2)/(k2-k1),那么判断(b1-b2)/(k2-k1)在左半区间还是右半区间递归下传即可;

   4.k1>k2同理。

注意:
1.会有除0的情况,所以把f1=f2的情况归到不等的情况里去判断,不然会RE出翔
2.对于交点的判断是对于dis[mid]的比较,而不是线段树区间mid的比较!
3.标记永久化感觉很玄妙啊,处理时要小心!
代码:

#include<bits/stdc++.h>
#define M 100003
#define inf 123456789123456789LL
#define LL long long
using namespace std;
int n,m,tot,cnt;
int dep[M],siz[M],son[M],top[M],pre[M],L[M],fa[M],first[M];
LL dis[M];
struct edge
{
    int u,v,w,next;
}e[M<<1];
struct Segment_tree
{
    bool lazy;
    LL minn,k,b; 
}tree[M<<2];
int in()
{
    char ch=getchar();int t=0,f=1;
    while (!isdigit(ch))
    {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (isdigit(ch)) t=(t<<3)+(t<<1)+ch-48,ch=getchar();
    return f*t;
}
void add(int x,int y,int z) {e[++tot]=(edge){x,y,z,first[x]};first[x]=tot;}
LL cal(int x,LL k,LL b){return dis[x]*k+b;}
void dfs1(int x)
{
    siz[x]=1;
    for (int i=first[x];i;i=e[i].next)
        if (e[i].v!=fa[x])
            dep[e[i].v]=dep[x]+1,
            fa[e[i].v]=x,
            dis[e[i].v]=dis[x]+e[i].w,
            dfs1(e[i].v),
            siz[x]+=siz[e[i].v],
            son[x]=(siz[e[i].v]>siz[son[x]]?e[i].v:son[x]);
}
void dfs2(int x,int tp)
{
    top[x]=tp;
    L[x]=++cnt;
    pre[cnt]=x;
    if (son[x]) dfs2(son[x],tp);
    for (int i=first[x];i;i=e[i].next)
        if (son[x]!=e[i].v&&fa[x]!=e[i].v) dfs2(e[i].v,e[i].v);
}
int LCA(int x,int y)
{
    int f1=top[x],f2=top[y];
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
        x=fa[f1];f1=top[x];
    }
    return dep[x]>dep[y]?y:x;
} 
void build(int rt,int begin,int end)
{
    tree[rt].minn=inf;
    if (begin==end) return;
    int mid=begin+end>>1;
    build(rt<<1,begin,mid);
    build(rt<<1|1,mid+1,end);
}
void pushup(int rt,int begin,int end)
{
    if (begin==end) tree[rt].minn=inf;
    else tree[rt].minn=min(tree[rt<<1].minn,tree[rt<<1|1].minn);
    if (tree[rt].lazy)
        tree[rt].minn=min(tree[rt].minn,
        min(tree[rt].k*dis[pre[begin]],tree[rt].k*dis[pre[end]])+tree[rt].b);
}
void update(int rt,int begin,int end,int l,int r,LL k,LL b)
{
    int mid=begin+end>>1;
    if (l<=begin&&end<=r)
        if (!tree[rt].lazy)
        {
            tree[rt].lazy=1;
            tree[rt].k=k;tree[rt].b=b;
            pushup(rt,begin,end);
            return;
        }
        else
        {
            LL f1=cal(pre[begin],tree[rt].k,tree[rt].b),
               f2=cal(pre[end],tree[rt].k,tree[rt].b),
               f3=cal(pre[begin],k,b),
               f4=cal(pre[end],k,b);
            if (f1>f3&&f2>f4)
                tree[rt].k=k,tree[rt].b=b;
            else if (f1<=f3&&f2<=f4) return;
            else
            {
                LL p=(b-tree[rt].b)/(tree[rt].k-k);
                if (tree[rt].k>k)
                {
                    if (p<=dis[pre[mid]])
                        swap(tree[rt].k,k),swap(tree[rt].b,b),
                        update(rt<<1,begin,mid,l,r,k,b);
                    else
                        update(rt<<1|1,mid+1,end,l,r,k,b);
                }
                else
                {
                    if (p>dis[pre[mid]])
                        swap(tree[rt].k,k),swap(tree[rt].b,b),
                        update(rt<<1|1,mid+1,end,l,r,k,b);
                    else
                        update(rt<<1,begin,mid,l,r,k,b);
                }
            }
            pushup(rt,begin,end);
            return;
        }
    if (mid>=l) update(rt<<1,begin,mid,l,r,k,b);
    if (mid<r)  update(rt<<1|1,mid+1,end,l,r,k,b);
    pushup(rt,begin,end);
}
LL get(int rt,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return tree[rt].minn;
    int mid=(begin+end)>>1;LL ans=inf; 
    if (tree[rt].lazy)
        ans=min(ans,tree[rt].b+
            min(dis[pre[max(l,begin)]]*tree[rt].k,
                dis[pre[min(r,end)]]*tree[rt].k));
    if (mid>=l) ans=min(ans,get(rt<<1,begin,mid,l,r));
    if (mid<r)  ans=min(ans,get(rt<<1|1,mid+1,end,l,r));
    return ans;
}
void solve(int x,int y)
{
    LL ans=inf;
    int f1=top[x],f2=top[y];
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
        ans=min(ans,get(1,1,n,L[f1],L[x]));
        x=fa[f1];f1=top[x];
    }
    if (dep[x]>dep[y]) swap(x,y);
    ans=min(ans,get(1,1,n,L[x],L[y]));
    printf("%lld\n",ans);
}
main()
{
    n=in();m=in();
    int opt,x,y,z,s,t;
    for (int i=1;i<n;i++)
        x=in(),y=in(),z=in(),
        add(x,y,z),add(y,x,z);
    dfs1(1);
    dfs2(1,1);
    build(1,1,n); 
    for (int i=1;i<=m;i++)
    {
        opt=in();s=in();t=in();
        if (opt==1)
        {
            x=in();y=in();
            int lca=LCA(s,t);
            LL  k1=-x,b1=dis[s]*x+y,
                k2=x,b2=dis[s]*x-dis[lca]*x*2+y;
            for (;top[s]!=top[lca];s=fa[top[s]])
                update(1,1,n,L[top[s]],L[s],k1,b1);
            update(1,1,n,L[lca],L[s],k1,b1);
            for (;top[t]!=top[lca];t=fa[top[t]])
                update(1,1,n,L[top[t]],L[t],k2,b2);
            update(1,1,n,L[lca],L[t],k2,b2);
        }
        else
            solve(s,t);
    }
}

你可能感兴趣的:(【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树))