【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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值