C120 树剖+李超树 P4069 [SDOI2016] 游戏

视频链接:C120 树剖+李超树 P4069 [SDOI2016] 游戏_哔哩哔哩_bilibili

 

 

 

 

D12 Luogu P3384【模板】轻重链剖分/树链剖分 - 董晓 - 博客园 (cnblogs.com) 

Luogu P4069 [SDOI2016] 游戏

// 树剖+李超树 O(nlognlognlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
#define ls u<<1
#define rs u<<1|1
const int N=100005;
const ll INF=123456789123456789;

int tot,head[N],to[N<<1],ne[N<<1],w[N<<1];
void add(int a,int b,int c){
  to[++tot]=b;w[tot]=c;
  ne[tot]=head[a];head[a]=tot;
}
int fa[N],son[N],dep[N],siz[N]; //树剖数组
int top[N],dfn[N],old[N],tim;
ll dis[N]; //到树根的距离

void dfs1(int u,int f){
  fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;
  for(int i=head[u];i;i=ne[i]){
    int v=to[i];
    if(v==f) continue;
    dis[v]=dis[u]+w[i];
    dfs1(v,u);
    siz[u]+=siz[v];
    if(siz[son[u]]<siz[v]) son[u]=v;
  }
}
void dfs2(int u,int tp){
  top[u]=tp,dfn[u]=++tim,old[tim]=u;
  if(son[u]) dfs2(son[u],tp);
  for(int i=head[u];i;i=ne[i]){
    int v=to[i];
    if(v==fa[u]||v==son[u])continue;
    dfs2(v,v);
  }
}
int LCA(int u,int v){
  while(top[u]!=top[v]){
    if(dep[top[u]]<dep[top[v]]) swap(u,v);
    u=fa[top[u]];
  }
  return dep[u]<dep[v]?u:v;
}

int n,m,cnt;
struct line{ //线段
  int k; ll b;
  line(){k=0,b=INF;}
  line(int x,ll y){k=x,b=y;}
}p[N<<1];

struct tree{ //李超树
  int id;    //优势线段的编号
  ll mi;     //最小值
  tree(){mi=INF;}
}tr[N<<3];   //因访问叶子节点的儿子,故开8倍空间

ll Y(int id,ll x){ //求Y值
  return p[id].k*x+p[id].b;
}
void pushup(int u,int l,int r){ //上传
  ll ans=min(Y(tr[u].id,dis[old[l]]),Y(tr[u].id,dis[old[r]]));
  tr[u].mi=min(ans,min(tr[ls].mi,tr[rs].mi));
}
void change(int u,int l,int r,int L,int R,int id){ //修改
  int mid=(l+r)>>1;
  if(L<=l&&r<=R){
    if(Y(id,dis[old[mid]])<Y(tr[u].id,dis[old[mid]])) swap(id,tr[u].id);
    if(Y(id,dis[old[l]])<Y(tr[u].id,dis[old[l]])) change(ls,l,mid,L,R,id);
    if(Y(id,dis[old[r]])<Y(tr[u].id,dis[old[r]])) change(rs,mid+1,r,L,R,id);
    pushup(u,l,r);
    return;
  }
  if(L<=mid) change(ls,l,mid,L,R,id);
  if(R>mid) change(rs,mid+1,r,L,R,id);
  pushup(u,l,r);
}
void treechange(int u,int v,int id){ //树修
  while(top[u]!=top[v]){
    if(dep[top[u]]<dep[top[v]]) swap(u,v);
    change(1,1,n,dfn[top[u]],dfn[u],id);
    u=fa[top[u]]; //爬链
  }
  if(dep[u]>dep[v]) swap(u,v);
  change(1,1,n,dfn[u],dfn[v],id); //最后一段
}
ll query(int u,int l,int r,int L,int R){ //查询
  if(L<=l&&r<=R) return tr[u].mi;
  int mid=(l+r)>>1;
  ll ans=min(Y(tr[u].id,dis[old[max(l,L)]]),
             Y(tr[u].id,dis[old[min(r,R)]]));
  if(L<=mid) ans=min(ans,query(ls,l,mid,L,R));
  if(R>mid) ans=min(ans,query(rs,mid+1,r,L,R));
  return ans;
}
ll treequery(int u,int v){ //树查
  ll ans=INF;
  while(top[u]!=top[v]){
    if(dep[top[u]]<dep[top[v]]) swap(u,v);
    ans=min(ans,query(1,1,n,dfn[top[u]],dfn[u]));
    u=fa[top[u]]; //爬链
  }
  if(dep[u]>dep[v]) swap(u,v);
  return min(ans,query(1,1,n,dfn[u],dfn[v]));
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1,u,v,w; i<=n-1; i++)
    scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
  dfs1(1,0),dfs2(1,1);
  for(int i=1,op,s,t; i<=m; i++){
    scanf("%d%d%d",&op,&s,&t);
    if(op==1){
      int a,b,lca;scanf("%d%d",&a,&b); lca=LCA(s,t);
      p[++cnt]=line(-a,a*dis[s]+b);
      treechange(s,lca,cnt);
      p[++cnt]=line(a,a*(dis[s]-2*dis[lca])+b);
      treechange(lca,t,cnt);
    }
    else printf("%lld\n",treequery(s,t));
  }
}

 

posted @ 2024-05-13 12:17  董晓  阅读(125)  评论(0编辑  收藏  举报