首先可以把修改轉換一下,因為那個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[lca]*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同理。
實際上這就是線段樹維護半平面交的過程~~~~~
詢問就簡單多了,直接用標記永久化的線段樹的方法更新即可。
AC代碼如下:
#include<iostream>#include<cstdio>#include<cstring>#define ll long long#define N 100005#define inf 123456789123456789LLusing namespace std;int n,m,tot,dfsclk,fst[N],pnt[N<<1],len[N<<1],nxt[N<<1],fa[N],son[N],sz[N],anc[N],pos[N],id[N];int lk[N<<2]; ll lb[N<<2],val[N<<2],d[N],ans; bool tag[N<<2];int read(){int x=0,fu=1; char ch=getchar();while (ch<'0' || ch>'9'){ if (ch=='-') fu=-1; ch=getchar(); }while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }return x*fu;}void add(int x,int y,int z){pnt[++tot]=y; len[tot]=z; nxt[tot]=fst[x]; fst[x]=tot;}void dfs(int x){int p; sz[x]=1;for (p=fst[x]; p; p=nxt[p]){int y=pnt[p];if (fa[x]!=y){fa[y]=x; d[y]=d[x]+len[p];dfs(y); sz[x]+=sz[y];if (sz[y]>sz[son[x]]) son[x]=y;}}}void divide(int x,int tp){pos[x]=++dfsclk; id[dfsclk]=x; anc[x]=tp;if (son[x]) divide(son[x],tp); int p;for (p=fst[x]; p; p=nxt[p]){int y=pnt[p];if (y!=fa[x] && y!=son[x]) divide(y,y);}}int lca(int x,int y){for (; anc[x]!=anc[y]; x=fa[anc[x]])if (d[anc[x]]<d[anc[y]]) swap(x,y);return (d[x]<d[y])?x:y;}void maintain(int k,int l,int r){if (l<r) val[k]=min(val[k<<1],val[k<<1|1]); else val[k]=inf;if (tag[k]) val[k]=min(val[k],min(d[id[l]]*lk[k],d[id[r]]*lk[k])+lb[k]);}void build(int k,int l,int r){val[k]=inf;if (l<r){int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r);}}void up(int k,int l,int r,int u,ll v){if (!tag[k]){ tag[k]=1; lk[k]=u; lb[k]=v; } else{ll x1=d[id[l]]*u+v,y1=d[id[r]]*u+v,x2=d[id[l]]*lk[k]+lb[k],y2=d[id[r]]*lk[k]+lb[k];int mid=(l+r)>>1;if (x1<=x2 && y1<=y2){lk[k]=u; lb[k]=v;} else if (x1>=x2 && y1>=y2) return;else if (u<lk[k]){ll tmp=(v-lb[k])/(lk[k]-u)+1;if (tmp<=d[id[mid]]){swap(u,lk[k]); swap(v,lb[k]);up(k<<1,l,mid,u,v);} else up(k<<1|1,mid+1,r,u,v);} else{ll tmp=(lb[k]-v-1)/(u-lk[k]);if (tmp>d[id[mid]]){swap(u,lk[k]); swap(v,lb[k]);up(k<<1|1,mid+1,r,u,v);} else up(k<<1,l,mid,u,v);}}maintain(k,l,r);}void ins(int k,int l,int r,int x,int y,int u,ll v){if (l==x && r==y){ up(k,l,r,u,v); return; }int mid=(l+r)>>1;if (y<=mid) ins(k<<1,l,mid,x,y,u,v); elseif (x>mid) ins(k<<1|1,mid+1,r,x,y,u,v); else{ins(k<<1,l,mid,x,mid,u,v); ins(k<<1|1,mid+1,r,mid+1,y,u,v);}maintain(k,l,r);}void qry(int k,int l,int r,int x,int y){if (l==x && r==y){ ans=min(ans,val[k]); return; }if (tag[k]) ans=min(ans,min(d[id[x]]*lk[k],d[id[y]]*lk[k])+lb[k]);int mid=(l+r)>>1;if (y<=mid) qry(k<<1,l,mid,x,y); elseif (x>mid) qry(k<<1|1,mid+1,r,x,y); else{qry(k<<1,l,mid,x,mid); qry(k<<1|1,mid+1,r,mid+1,y);}}int main(){n=read(); m=read(); int i,x,y,z;for (i=1; i<n; i++){x=read(); y=read(); z=read();add(x,y,z); add(y,x,z);}dfs(1); divide(1,1); build(1,1,n); int k,u,v; ll tmp;while (m--){k=read();if (k==1){x=read(); y=read(); u=read(); v=read(); z=lca(x,y);tmp=d[x]*u+v;for (; anc[x]!=anc[z]; x=fa[anc[x]])ins(1,1,n,pos[anc[x]],pos[x],-u,tmp);ins(1,1,n,pos[z],pos[x],-u,tmp);tmp-=(d[z]<<1)*u;for (; anc[y]!=anc[z]; y=fa[anc[y]])ins(1,1,n,pos[anc[y]],pos[y],u,tmp);ins(1,1,n,pos[z],pos[y],u,tmp);} else{x=read(); y=read(); ans=inf;for (; anc[x]!=anc[y]; x=fa[anc[x]]){if (d[anc[x]]<d[anc[y]]) swap(x,y);qry(1,1,n,pos[anc[x]],pos[x]);}if (d[x]>d[y]) swap(x,y); qry(1,1,n,pos[x],pos[y]);printf("%lld\n",ans);}}return 0;}
by lych
2016.4.15