【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);
    }
}
时间: 2024-08-05 01:57:53

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

HDU - 3966 Aragorn&#39;s Story(树链剖分入门+线段树)

HDU - 3966 Aragorn's Story Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of ene

[BZOJ2243]SDOI2011染色|树链剖分|LCT

裸题嘛.. 先考虑一条线段上如何查询颜色段数,只要对每个线段树节点多维护一个左颜色和右颜色,然后合并的时候sum[x]=sum[lc]+sum[rc]-(左儿子的右颜色==右儿子的左颜色)..实在太久没写树剖结果码+调试花了两节多晚自习,,各种傻逼错误,什么反向边忘加,标记忘记下传...还有就是更新答案的时候,关键的一点是要保证当前的两点(也就是a,b)是没有被更新到的,否则很难搞.. 表示LCT要更好写..不过在BZOJ上我的树链剖分6000+MS,LCT要13000+MS.. 树链剖分: #

【模板时间】◆模板&#183;II◆ 树链剖分

[模板·II]树链剖分 学长给我讲树链剖分,然而我并没有听懂,还是自学有用--另外感谢一篇Blog +by 自为风月马前卒+ 一.算法简述 树链剖分可以将一棵普通的多叉树转为线段树计算,不但可以实现对一棵子树的操作,还可以实现对两点之间路径的操作,或是求 LCA(看起来很高级). 其实树链剖分不算什么特别高难的算法,它建立在 LCA.线段树.DFS序 的基础上(如果不了解这些算法的还是先把这些算法学懂再看树链剖分吧 QwQ).又因为树链剖分的基础算法不难,树链剖分的题也逐渐被引入 OI 赛中.

Hdu 3699 Aragorn&#39;s Story (树链剖分)

题目大意: 对一颗树上进行路径加减,然后询问单点的值. 思路分析: 简单的树链剖分模板题. #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #pragma comment(linker,"/STACk:10240000,10240000") #define maxn 50005 #define lson num<<1,s

数据结构之树链剖分

首先了解一下基本概念: 重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子.      轻儿子:v的其它子节点.      重边:点v与其重儿子的连边.      轻边:点v与其轻儿子的连边.      重链:由重边连成的路径.      轻链:轻边. 剖分后的树有如下性质:      性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v]:      性质2:从根到某一点的路径上轻链.重链的个数都不大于logN. 树链剖分,如其字面意思,就是将一棵树按照轻重

codevs 1228 苹果树 树链剖分讲解

题目:codevs 1228 苹果树 链接:http://codevs.cn/problem/1228/ 看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了. 树链剖分的功能:快速修改,查询树上的路径. 比如一颗树 首先,我们要把树剖分成树链.定义: fa[x]是x节点的上一层节点(就是他的爸爸). deep[x]是x节点的深度. num[x]是x节点下面的子节点的数量(包括自己) son[x]重儿子:一个节点的儿子的num[x]值最大的节点.其他的儿子都是轻儿子. 重链:重儿子连接在一起

从lca到树链剖分 bestcoder round#45 1003

bestcoder round#45 1003 题,给定两个点,要我们求这两个点的树上路径所经过的点的权值是否出现过奇数次.如果是一般人,那么就是用lca求树上路径,然后判断是否出现过奇数次(用异或),高手就不这么做了,直接树链剖分.为什么不能用lca,因为如果有树退化成链,那么每次询问的复杂度是O(n), 那么q次询问的时间复杂度是O(qn) 什么是树链剖分呢? 就是把树的边分成轻链和重链 http://blogsina.com.cn/s/blog_6974c8b20100zc61.htmlh

BZOJ 2243 染色 | 树链剖分模板题进阶版

BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上还是原树上,把两个区间的信息合并的时候,要注意中间相邻两个颜色是否相同. 这代码好长啊啊啊啊 幸好一次过了不然我估计永远也De不出来 #include <cstdio> #include <cstring> #include <algorithm> using namesp

CF 191C Fools and Roads lca 或者 树链剖分

They say that Berland has exactly two problems, fools and roads. Besides, Berland has n cities, populated by the fools and connected by the roads. All Berland roads are bidirectional. As there are many fools in Berland, between each pair of cities th