P4069 [SDOI2016]遊戲 樹剖+李超樹
阿新 • • 發佈:2020-12-13
題意:
分析:
首先樹上路徑操作,要用樹剖
其次,操作等價於區間加一條線段,然後查詢區間最小值
但是與普通李超樹不同,這次 \(x\) 值並不連續,每次插入的線段下標是和給定起點的距離,那麼我們更改一下維護的資訊,線段樹上 \(a\) 點的 $x $ 值是它離根的距離,然後我們將操作轉化,每次插入的線段分成兩條
- \(s\to lca\)
這一部分距離隨編號減小而增大,所以斜率由 \(k\) 變為 \(-k\),\(b\) 的初值就是 \(dis[s]*k+b\)
- \(lca\to t\)
這一部分距離隨編號增大而增大,,那麼斜率不變, \(b\) 值變為 \((dis[s]-(dis[lca]*2)*k+b)\)
之後就是維護區間最小值,但注意由於標記永久化的操作,我們需要每一次將答案和對應區間內 \(max(l,ql)\) ~ \(min(r,qr)\) 的部分的最小值取 \(min\)
程式碼:
#include<bits/stdc++.h> #define lc (rt<<1) #define rc (rt<<1)|1 using namespace std; namespace zzc { long long read() { long long x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } const int maxn = 1e5+5; long long n,m,cnt,idx; long long head[maxn],siz[maxn],fa[maxn],son[maxn],top[maxn],dep[maxn],dfn[maxn],dis[maxn],id[maxn]; struct edge { int to,nxt,val; }e[maxn<<1]; void add(int u,int v,int w) { e[++cnt].to=v; e[cnt].nxt=head[u]; e[cnt].val=w; head[u]=cnt; } struct line { long long k,b; line (long long k=0,long long b=0):k(k),b(b){} long long calc(long long pos) { return k*pos+b; } }; struct node { line L; long long val; bool flag; node(line L,long long val,bool flag=false):L(L),val(val),flag(flag){} node(){} }t[maxn<<2]; void dfs1(int u,int ff) { dep[u]=dep[ff]+1;fa[u]=ff;siz[u]=1;son[u]=0; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(v==ff) continue; dis[v]=dis[u]+e[i].val; dfs1(v,u); siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } void dfs2(int u,int bel) { top[u]=bel;dfn[u]=++idx;id[idx]=u; if(son[u]) dfs2(son[u],bel); for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } void pushup(int rt) { t[rt].val=min(t[rt].val,min(t[lc].val,t[rc].val)); } void build(int rt,int l,int r) { t[rt]=node(line(0,123456789123456789ll),123456789123456789ll,true); if(l==r) return ; int mid=(l+r)>>1; build(lc,l,mid);build(rc,mid+1,r); pushup(rt); } int lca(int x,int y) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } return dep[x]>dep[y]?y:x; } void update(int rt,int l,int r,int ql,int qr,line x) { if(ql<=l&&r<=qr) { long long ll=dis[id[l]]; long long rr=dis[id[r]]; long long mid=(l+r)>>1,mmid=dis[id[mid]]; long long lp=x.calc(ll),rp=x.calc(rr); long long lq=t[rt].L.calc(ll),rq=t[rt].L.calc(rr); if(!t[rt].flag) t[rt].flag=true,t[rt].L=x; else if(lp<lq&&rp<rq) t[rt].L=x; else if(lp<lq||rp<rq) { if(x.calc(mmid)<t[rt].L.calc(mmid)) swap(x,t[rt].L); if(x.calc(ll)<t[rt].L.calc(ll)) update(lc,l,mid,ql,qr,x); else update(rc,mid+1,r,ql,qr,x); } t[rt].val=min(t[rt].val,min(t[rt].L.calc(ll),t[rt].L.calc(rr))); if(l!=r) pushup(rt); return ; } int mid=(l+r)>>1; if(ql<=mid) update(lc,l,mid,ql,qr,x); if(qr>mid) update(rc,mid+1,r,ql,qr,x); if(l!=r) pushup(rt); } long long query(int rt,int l,int r,int ql,int qr) { if(ql<=l&&r<=qr) return t[rt].val; int mid=(l+r)>>1; long long res=123456789123456789ll; if(t[rt].flag) res=min(res,min(t[rt].L.calc(dis[id[max(l,ql)]]),t[rt].L.calc(dis[id[min(r,qr)]]))); if(ql<=mid) res=min(res,query(lc,l,mid,ql,qr)); if(qr>mid) res=min(res,query(rc,mid+1,r,ql,qr)); return res; } void update_tree(int u,int v,long long k,long long b) { line x=line(k,b); while(top[u]!=top[v]) { update(1,1,n,dfn[top[u]],dfn[u],x); u=fa[top[u]]; } update(1,1,n,dfn[v],dfn[u],x); } long long query_tree(int s,int t) { long long res=123456789123456789; while(top[s]!=top[t]) { if(dep[top[s]]<dep[top[t]]) swap(s,t); res=min(res,query(1,1,n,dfn[top[s]],dfn[s])); s=fa[top[s]]; } if(dep[s]>dep[t]) swap(s,t); res=min(res,query(1,1,n,dfn[s],dfn[t])); return res; } void work() { long long opt,s,t,a,b,c; n=read();m=read(); for(int i=1;i<n;i++) { a=read();b=read();c=read(); add(a,b,c);add(b,a,c); } dfs1(1,0);dfs2(1,1); build(1,1,n); for(int i=1;i<=m;i++) { opt=read();s=read();t=read(); if(opt==1) { a=read();b=read(); int tmp=lca(s,t); update_tree(s,tmp,-a,dis[s]*a+b); update_tree(t,tmp,a,(dis[s]-(dis[tmp]<<1))*a+b); } else { printf("%lld\n",query_tree(s,t)); } } } } int main() { zzc::work(); return 0; }