[SDOI2016]游戏(树链剖分,李超线段树模板)

Posted hsez-cyx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2016]游戏(树链剖分,李超线段树模板)相关的知识,希望对你有一定的参考价值。

[SDOI2016]游戏(luogu)

Solution

对于一次Alice的操作,设 lca 为 s , t 的 LCA ,dis [ i ] 为点 i 到根的路径长度

则 s 到 lca 的路径上每个点 i 添加一个数字

$$-a*disleft[i ight]+(a*disleft[s ight]+b)$$

t 到 lca 的路径上每个点 i 添加一个数字

$$a*disleft[i ight]+a*(disleft[s ight]-2*disleft[lca ight])$$

可以看出树剖+李超线段树的模型

李超线段树——在平面直角坐标系中插入一些线段,维护这些线段上某整数横坐标的最大/最小纵坐标

原理

先按普通线段树建树,树上每个节点维护两个标记:

  • t:完全覆盖该节点维护的区间,且使该节点维护区间中点处对应纵坐标最大/最小的线段编号
  • mn:整个该节点维护区间内最大/最小纵坐标

其中t标记永久化,只有被取代时下传

实现见代码

Code

 

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int N=4e5+10,M=4e5+10;
const ll inf=123456789123456789;
ll w,dis[N],edge[M],a,b;
int n,m,u,v,head[N],ver[M],id,nxt[M],cnt,sum,rt,opt;
int dep[N],dfn[N],son[N],si[N],re[N],top[N],fa[N],tot;
struct line
{
    ll k,b;
}p[M];
struct node
{
    int lc,rc,l,r,t;
    ll mn;
}f[N>>1];
void addedge(int u,int v,ll w)
{
    ver[++cnt]=v,nxt[cnt]=head[u],edge[cnt]=w,head[u]=cnt;
}
void dfs1(int u,int fu)
{
    si[u]=1,fa[u]=fu,dep[u]=dep[fu]+1;
    for(int i=head[u],v;i;i=nxt[i])
    {
        v=ver[i];
        if(v==fu) continue;
        dis[v]=dis[u]+edge[i];
        dfs1(v,u),si[u]+=si[v];
        if(!son[u] || si[son[u]]<si[v]) son[u]=v;
    }
}
void dfs2(int u,int fu)
{
    re[dfn[u]=++tot]=u;
    if(!son[u]) return;
    top[son[u]]=top[u],dfs2(son[u],u);
    for(int i=head[u],v;i;i=nxt[i])
    {
        v=ver[i];
        if(v==fu || v==son[u]) continue;
        top[v]=v,dfs2(v,u);
    }
}
inline ll calc(int x,int y)
{
    return p[y].k*dis[re[x]]+p[y].b;
}
void build(int &g,int l,int r)
{
    g=++sum;
    f[g].mn=inf,f[g].t=1;
    f[g].l=l,f[g].r=r;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(f[g].lc,l,mid);
    build(f[g].rc,mid+1,r);
}
int lca(int x,int y)
{
    int px=top[x],py=top[y];
    while(px!=py)
        if(dep[px]>dep[py]) x=fa[px],px=top[x];
        else y=fa[py],py=top[y];
    return dep[x]<dep[y]?x:y;
}
void push_up(int g)
{
    f[g].mn=min(f[g].mn,min(f[f[g].lc].mn,f[f[g].rc].mn));
}
void add(int g,int l,int r,int x)
{
    int gl=f[g].l,gr=f[g].r,mid=(gl+gr)>>1,t=f[g].t;
    if(l<=gl && r>=gr)
    {
        if(calc(gl,x)>=calc(gl,t) && calc(gr,x)>=calc(gr,t)) return;
        f[g].mn=min(f[g].mn,min(calc(gl,x),calc(gr,x)));
        if(calc(gl,x)<=calc(gl,t) && calc(gr,x)<=calc(gr,t))
        {
            f[g].t=x;
            return ;
        }
        if(p[x].k<p[t].k)
        {
            if(calc(mid,x)<=calc(mid,t)) add(f[g].lc,l,r,f[g].t),f[g].t=x;
            else add(f[g].rc,l,r,x);
        }
        else
        {
            if(calc(mid,x)<=calc(mid,t)) add(f[g].rc,l,r,f[g].t),f[g].t=x;
            else add(f[g].lc,l,r,x);
        }
        push_up(g);
        return ;
    }
    if(r<=mid) add(f[g].lc,l,r,x);
    else if(l>mid) add(f[g].rc,l,r,x);
    else add(f[g].lc,l,mid,x),add(f[g].rc,mid+1,r,x);
    push_up(g);
}
void Add(int x,int y)
{
    while(top[x]!=top[y]) add(rt,dfn[top[x]],dfn[x],id),x=fa[top[x]];
    add(rt,dfn[y],dfn[x],id);
}
ll get(int g,int l,int r)
{
    if(f[g].l>=l && f[g].r<=r) return f[g].mn;
    ll ans=inf;
    int mid=(f[g].l+f[g].r)>>1;
    if(p[f[g].t].b!=inf) ans=min(calc(max(l,f[g].l),f[g].t),calc(min(r,f[g].r),f[g].t));
    if(r<=mid) return min(ans,get(f[g].lc,l,r));
    else if(l>mid) return min(ans,get(f[g].rc,l,r));
    else return min(ans,min(get(f[g].lc,l,mid),get(f[g].rc,mid+1,r)));
}
ll Get(int x,int y)
{
    int px=top[x],py=top[y];
    ll ans=inf;
    while(px!=py)
        if(dep[px]>dep[py]) ans=min(ans,get(rt,dfn[px],dfn[x])),x=fa[px],px=top[x];
        else ans=min(ans,get(rt,dfn[py],dfn[y])),y=fa[py],py=top[y];
    if(dfn[x]<dfn[y]) ans=min(ans,get(rt,dfn[x],dfn[y]));
    else ans=min(ans,get(rt,dfn[y],dfn[x]));
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%lld",&u,&v,&w);
        addedge(u,v,w),addedge(v,u,w);
    }
    dfs1(1,0),top[1]=1,dfs2(1,0);
    p[++id]=(line){0,inf};
    build(rt,1,n);
    while(m--)
    {
        scanf("%d%d%d",&opt,&u,&v);
        if(opt==1)
        {
            int la=lca(u,v);
            scanf("%lld%lld",&a,&b);
            p[++id]=(line){-a,a*dis[u]+b};
            Add(u,la);
            p[++id]=(line){a,a*(dis[u]-(dis[la]<<1))+b};
            Add(v,la);
        }
        else printf("%lld
",Get(u,v));
    }
    return 0;
}

 

以上是关于[SDOI2016]游戏(树链剖分,李超线段树模板)的主要内容,如果未能解决你的问题,请参考以下文章

P4069 [SDOI2016]游戏 [李超树,树链剖分]

数据结构(树链剖分,线段树):SDOI 2016 游戏

BZOJ4515[Sdoi2016]游戏 树链剖分+线段树

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

BZOJ 2243: [SDOI2011]染色 树链剖分+线段树区间合并

bzoj2243: [SDOI2011]染色--线段树+树链剖分