【BZOJ 4515: [Sdoi2016]游戏】树剖维护李超树(维护凸壳)
晚上OB考了我们两道题,然而两道题我都感觉被卡住,知道算法之后到凌晨才调完,真是太弱了orz
BZOJ4515
4515: [Sdoi2016]游戏
Time Limit: 40 Sec Memory Limit: 256 MB Submit: 1005 Solved: 386 [Submit][Status][Discuss]Description
Alice 和 Bob 在玩一个游戏。
游戏在一棵有 n 个点的树上进行。最初,每个点上都只有一个数字,那个数字是 123456789123456789。
有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字。对于路径上的一个点 r,
若 r 与 s 的距离是 dis,那么 Alice 在点 r 上添加的数字是 a×dis+b。有时,Bob 会选择一条从 s 到 t 的路径。
他需要先从这条路径上选择一个点,再从那个点上选择一个数字。
Bob 选择的数字越小越好,但大量的数字让 Bob 眼花缭乱。Bob 需要你帮他找出他能够选择的最小的数字。
Input
第一行两个数字 n、m,表示树的点数和进行的操作数。
接下来 n−1 行,每行三个数字 u、v、w,表示树上有一条连接 u、v 的边,长度是 w。
接下来 m 行。每行第一个数字是 1 或 2。
若第一个数是 1,表示 Alice 进行操作,接下来四个数字 s、t、a、b。
若第一个数是 2,表示 Bob 进行操作,接下来四个数字 s、t。
Output
每当 Bob 进行操作,输出一行一个数,表示他能够选择的最小的数字
Sample Input
3 5
1 2 10
2 3 20
2 1 3
1 2 3 5 6
2 2 3
1 2 3 -5 -6
2 2 3
Sample Output
123456789123456789
6
-106
HINT
n≤100000,m≤100000,∣a∣≤10000,0<=w,|b|<=10^9
Source
我们发现,如果我们将深度看作是函数的自变量,并且我们将一条路径拆分成分别到LCA的两条路径,那么我们发现他们可以各自表示成一个与深度相关的线段。这样我们发现最后就是维护一个凸壳,那么我们利用李超树+树剖来维护即可。注意我们可以一条路径开一棵线段树,这样我们的时间复杂度就可以到达(nlog^2)(因为李超树时间复杂度为nlog^2,易发现,对于路径中途都是完全覆盖地下放为logn乘上树剖地log)。 记得判断各种神奇的边界条件,由于李超树是永久化标记,那么我们在查询的时候要顺便检索一下查询区间与当前节点交中的最小值。‘ 写得相当的丑的代码:#include<stdio.h> #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<vector> #define int long long using namespace std; const int maxn = 200005; typedef long long ll; const ll inf = 123456789123456789; int n,m; vector<int>ve[maxn]; struct node{ node *ls,*rs; ll k,b,mx; }z[maxn*2],*rt[maxn]; int tot,bot[maxn]; int en[maxn],nt[maxn],la[maxn],owo,len[maxn],fa[maxn]; ll dep[maxn]; int tof[maxn]; int top[maxn],siz[maxn],zerz[maxn],dfn[maxn]; void adg(int x,int y,int z) { en[++owo]=y; nt[owo]=la[x]; la[x]=owo; len[owo] = z; } char buf[1<<20],*p1,*p2; #define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++) inline int R() { char t=GC; int x=0; bool fl = 1; while((!isdigit(t))&&(t!='-')) t=GC; if(t=='-') fl=0,t='0'; while(isdigit(t)) x=x*10+t-48,t=GC; return fl?x:-x; } void dfs1(int x,int ba) { fa[x] = ba; siz[x] = 1; for(int it=la[x];it;it=nt[it]) { int y = en[it]; if(y==ba) continue; dep[y] = dep[x] + len[it]; tof[y] = len[it]; dfs1(y,x); siz[x] += siz[y]; if(siz[y] > siz[zerz[x]]) zerz[x] = y; } } void maketree(node *&p,int l,int r) { p = &z[++tot]; if(l==r) { p->k = 0; p->mx = p->b = inf; return; } int mid = (l+r)>>1; maketree(p->ls,l,mid); maketree(p->rs,mid+1,r); p->k = 0; p->mx = p->b = inf; return; } ll gety(ll x,ll K,ll B) { return K*x + B; } void dfs2(int x,int ace) { ve[ace].push_back(x); top[x] = ace; bot[ace] = x; if(ace==x) dfn[x] = 1; else dfn[x] = dfn[fa[x]] + 1; if(zerz[x]) dfs2(zerz[x],ace); for(int it=la[x];it;it=nt[it]) { int y = en[it]; if(y==fa[x]||y==zerz[x]) continue; dfs2(y,y); } if(x==ace) maketree(rt[ace],1,dfn[bot[ace]]); } int getlca(int x,int y) { while(top[x]!=top[y]) { dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]; } return dep[x]<dep[y]?x:y; } ll getmin(int tp,node *&p,int l,int r,int x,int y) { int lma = max(x,l); int rmi = min(r,y); ll hs = inf; if(lma<=y) hs = min(hs , gety(dep[ve[tp][lma-1]],p->k,p->b) ); if(rmi>=x) hs = min(hs , gety(dep[ve[tp][rmi-1]],p->k,p->b) ); if(x<=l&&r<=y) { return min(hs,p->mx);} int mid = (l+r)>>1; if(x>mid) return min(hs,getmin(tp,p->rs,mid+1,r,x,y)); else if(y<=mid) return min(hs,getmin(tp,p->ls,l,mid,x,y) ); else return min(hs,min(getmin(tp,p->ls,l,mid,x,y),getmin(tp,p->rs,mid+1,r,x,y)) ); } ll query(int x,int y) { ll ans = inf; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); ans = min(ans,getmin(top[x],rt[top[x]],1,dfn[bot[top[x]]],1,dfn[x])); x = fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); ans = min(ans,getmin(top[x],rt[top[x]],1,dfn[bot[top[x]]],dfn[x],dfn[y])); return ans; } int bigger(ll kx,ll bx,ll ky,ll by,ll L,ll R) { ll yxl = L*kx+bx; ll yxr = R*kx+bx; ll yyl = L*ky+by; ll yyr = R*ky+by; if(yxl<=yyl&&yxr<=yyr) return 1; else if(yxl>=yyl&&yxr>=yyr) return -1; return 0; } double getjiao(ll ka,ll ba,ll kb,ll bb) { return (double)(1.0*bb-1.0*ba)/(1.0*ka - 1.0*kb); } void upd(node *&p,int l,int r) { p->mx = min( min(p->mx,gety(dep[l],p->k,p->b) ), gety(dep[r],p->k,p->b) ); if(p->ls!=NULL) p->mx = min(p->mx,p->ls->mx); if(p->rs!=NULL) p->mx = min(p->mx,p->rs->mx); } void lcs(int tp,node *&p,int l,int r,int x,int y,ll K,ll B) { int mid = (l+r)>>1; if(x<=l&&r<=y){ if(p->k==K&&p->b==B) return; int jg = bigger(p->k,p->b,K,B,dep[ve[tp][l-1]],dep[ve[tp][r-1]]); if(jg==1) return; else if(jg==-1) { p->k = K; p->b = B; upd(p,ve[tp][l-1],ve[tp][r-1]); return; } else { if(l==r) return; if(K<p->k) { ll tmp = (B-p->b)/(p->k-K)+1; if(tmp<=dep[ve[tp][mid-1]]) { swap(K,p->k); swap(B,p->b); lcs(tp,p->ls,l,mid,x,y,K,B); } else lcs(tp,p->rs,mid+1,r,x,y,K,B); } else { ll tmp = (B-p->b-1)/(p->k-K); if(tmp>dep[ve[tp][mid-1]]) { swap(p->k,K); swap(B,p->b); lcs(tp,p->rs,mid+1,r,x,y,K,B); } else lcs(tp,p->ls,l,mid,x,y,K,B); } } upd(p,ve[tp][l-1],ve[tp][r-1]); return; } if(x>mid) lcs(tp,p->rs,mid+1,r,x,y,K,B); else if(y<=mid) lcs(tp,p->ls,l,mid,x,y,K,B); else lcs(tp,p->ls,l,mid,x,y,K,B) , lcs(tp,p->rs,mid+1,r,x,y,K,B); upd(p,ve[tp][l-1],ve[tp][r-1]); } void change(int x,int y,ll a,ll b) { while(top[x]!=top[y]) { ll dd = dep[x]; ll B = b + a*dd; ll K = -a; lcs(top[x],rt[top[x]],1,dfn[bot[top[x]]],1,dfn[x],K,B); int o = fa[top[x]]; b += (dep[x]-dep[o])*a; x = o; } ll dd = dep[x]; ll B = b + a*dd; ll K = -a; lcs(top[x],rt[top[x]],1,dfn[bot[top[x]]],dfn[y],dfn[x],K,B); return; } main() { // freopen("ouo.in","r",stdin); // freopen("ouo.out","w",stdout); n=R(); m=R(); for(int i=1;i<n;i++) { int x,y,z; x=R(); y=R(); z=R(); adg(x,y,z); adg(y,x,z); } dfs1(1,0); dfs2(1,1); for(int i=1;i<=m;i++) { int op; op=R(); if(op==1) { int s,t,a,b; s=R(); t=R(); a=R(); b=R(); int lcc = getlca(s,t); change(s,lcc,a,b); ll dd = dep[s] + dep[t] - 2*dep[lcc]; b = a*dd + b; a = -a; change(t,lcc,a,b); } else { int s,t; s=R(); t=R(); printf("%lld\n",query(s,t)); } } }