[BZOJ4515][SDOI2016]游戏(线段树+链剖+差分)

本文探讨了一种结合树链剖分与线段树优化的技术来解决特定类型的路径查询问题。通过分析距离计算及路径上的最小值求解过程,文章详细介绍了如何利用线段树进行区间更新和查询,特别关注于上升和下降路径的处理。

题目:

我是超链接

题解:

我们先随意分析一下题目,这个处理路径上的最小值应该是树链剖分。
还有这个距离,一开始想的是lca,但我发现这个s作为一条路径的开始不能白费,而且对于每个节点和s求lca显然太慢了,我们考虑差分,即:
上升的路径: A(dis[s]dis[r])+B=Adis[r]+Adis[s]+B A ( d i s [ s ] − d i s [ r ] ) + B = − A d i s [ r ] + A d i s [ s ] + B
下降的路径: A(dis[s]+dis[r]2dis[lca])+B=Adis[r]+Adis[s]2Adis[lca]+B A ( d i s [ s ] + d i s [ r ] − 2 ∗ d i s [ l c a ] ) + B = A d i s [ r ] + A d i s [ s ] − 2 ∗ A d i s [ l c a ] + B
那么lca就是固定的了
这里我们发现是一个一次函数的样子:dis[r]作为未知数
那么对于节点求一次函数的最值我们有思路:前几题刚做的标记不下放的线段树
而且可以发现,用dfs序建立出来的线段树的每一段dis肯定递增/递减,也就是说x递增/减,我们依然可以通过增减性描述一段函数,所以还是套路,两端都大于走人,都小于修改
这里还需要update一个minn值,因为有区间查询嘛,但是我们的不下放标记怎么办呢?其实在查询的时候有这样一句话就肯定可以取到区间内的最值了

answer=min(answer,min(t[now].a*dis[num[max(l,lrange)]],t[now].a*dis[num[min(r,rrange)]])+t[now].b);

我们如何判断一条路径是上升还是下降呢?其实并不需要判断,只需要分成两段做就好了

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long 
using namespace std;
const LL inf=123456789123456789LL;
const int N=100005;
const int sz=18;
struct hh{LL a,b;bool tag;}t[N*4];////这里别忘了longlong。。
int tot,nxt[N*2],point[N],c[N*2],v[N*2],f[N][sz+5],size[N],son[N],h[N],mi[sz+5],fa[N],top[N],in[N],cnt,num[N],n;
LL ans,minn[N*4],dis[N];
void addline(int x,int y,int z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void dfs_1(int x,int father)
{
    fa[x]=father;size[x]=1;int maxx=0;h[x]=h[father]+1;
    for (int i=1;i<sz;i++)
      if (h[x]<mi[i]) break;
      else f[x][i]=f[f[x][i-1]][i-1];
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=father) 
      {
        dis[v[i]]=dis[x]+(LL)c[i];
        f[v[i]][0]=x;
        dfs_1(v[i],x);
        size[x]+=size[v[i]];
        if (maxx<size[v[i]]) maxx=size[v[i]],son[x]=v[i];
      }
}
void dfs_2(int x,int fa)
{
    if (son[fa]==x) top[x]=top[fa];
    else top[x]=x;
    in[x]=++cnt;
    if (son[x])
    {
        dfs_2(son[x],x);
        for (int i=point[x];i;i=nxt[i])
          if (v[i]!=son[x] && v[i]!=fa) dfs_2(v[i],x);
    }
}
void updata(int now){minn[now]=min(minn[now],min(minn[now<<1],minn[now<<1|1]));}
void build(int now,int l,int r)
{
    t[now].tag=0;
    minn[now]=inf;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
}
LL Y(LL x,LL a,LL b){return a*x+b;}
int lca(int x,int y)
{
    if (h[x]<h[y]) swap(x,y);
    int k=h[x]-h[y];
    for (int i=0;i<sz;i++)
      if (k>>i&1) x=f[x][i];
    if (x==y) return x;
    for (int i=sz-1;i>=0;i--)
      if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
void add(int now,int l,int r,int lrange,int rrange,LL a,LL b)
{
    int mid=(l+r)>>1;
    if (lrange<=l && rrange>=r)
    {
        LL nl=Y(dis[num[l]],a,b),nr=Y(dis[num[r]],a,b);
        LL ll,lr;
        if (!t[now].tag) ll=inf,lr=inf;
        else ll=Y(dis[num[l]],t[now].a,t[now].b),lr=Y(dis[num[r]],t[now].a,t[now].b);
        if (nl<=ll && nr<=lr) {t[now].a=a;t[now].b=b;t[now].tag=1;minn[now]=min(minn[now],min(nl,nr));return;}
        if (nl>=ll && nr>=lr) return;
        add(now<<1,l,mid,lrange,rrange,a,b);
        add(now<<1|1,mid+1,r,lrange,rrange,a,b);
    }
    else
    {
        if (lrange<=mid) add(now<<1,l,mid,lrange,rrange,a,b);
        if (rrange>mid) add(now<<1|1,mid+1,r,lrange,rrange,a,b);
    }
    updata(now);
}
LL findmin(int now,int l,int r,int lrange,int rrange)
{
    if (lrange<=l && rrange>=r) return minn[now];
    int mid=(l+r)>>1;LL answer=inf;
    if (t[now].tag) answer=min(answer,min(t[now].a*dis[num[max(l,lrange)]],t[now].a*dis[num[min(r,rrange)]])+t[now].b);///
    if (lrange<=mid) answer=min(answer,findmin(now<<1,l,mid,lrange,rrange));
    if (rrange>mid) answer=min(answer,findmin(now<<1|1,mid+1,r,lrange,rrange));
    return answer;
}
void solve(int u,int v,LL a,LL b)
{
    int f1=top[u],old=lca(u,v),f2=top[old],s=u;
    while (f1!=f2)
    {
        add(1,1,n,in[f1],in[u],-a,a*dis[s]+b);  
        u=fa[f1];f1=top[u];
    }
    if (in[u]>in[old]) add(1,1,n,in[old],in[u],-a,a*dis[s]+b);
    else add(1,1,n,in[u],in[old],-a,a*dis[s]+b);

    f1=top[v];
    while (f1!=f2)
    {
        add(1,1,n,in[f1],in[v],a,a*dis[s]-2LL*a*dis[old]+b);
        v=fa[f1];f1=top[v];
    }
    if (in[old]>in[v]) add(1,1,n,in[v],in[old],a,a*dis[s]-2LL*a*dis[old]+b);
    else add(1,1,n,in[old],in[v],a,a*dis[s]-2LL*a*dis[old]+b);
}
LL qurry(int u,int v)
{
    ans=inf;
    int f1=top[u],f2=top[v];
    while (f1!=f2)
    {
        if (h[f1]<h[f2]) swap(f1,f2),swap(u,v);
        ans=min(ans,findmin(1,1,n,in[f1],in[u]));
        u=fa[f1];f1=top[u];
    }
    if (in[u]>in[v]) swap(u,v);
    ans=min(ans,findmin(1,1,n,in[u],in[v]));
    return ans;
}
int main()
{
    mi[0]=1;for (int i=1;i<sz;i++) mi[i]=mi[i-1]*2;
    int m;LL a,b;scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++)
    {
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        addline(x,y,z);
    }
    dfs_1(1,0);
    dfs_2(1,0);
    for (int i=1;i<=n;i++) num[in[i]]=i;
    build(1,1,n);
    for (int i=1;i<=m;i++)
    {
        int id,s,t;scanf("%d%d%d",&id,&s,&t);
        if (id==2) printf("%lld\n",qurry(s,t));
        else
        {
            scanf("%lld%lld",&a,&b);
            solve(s,t,a,b);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值