bzoj4515: [Sdoi2016]游戏
2017-02-19 16:54
190 查看
链接
http://www.lydsy.com/JudgeOnline/problem.php?id=4515题解
这是一道神题!对于一次修改(s,t,a,b),设s和t的最近公共祖先是lca。可以推出,当x节点在s到lca的路径上时,x节点上要添加的数字就是−a∗dist[x]+a∗dist[s]+b;当x节点在lca到t的路径上时,这个节点要添加的数字是a∗dist[x]+a∗dist[s]−2∗a∗dist[lca]+b。
树剖转成序列,问题就变成了:支持给一个区间添加一条线段,询问区间最小值。
使用线段树来维护。
每一个节点上,都有且仅有一条线段,这条线段用a、b、dl、dr来描述,分别表示斜率、截距、左端点和右端点的自变量。
可以使用标记永久化,即没有下放标记的操作。线段树中记录了子树的最小值,使用传统的线段树查询时,当l<=p->l且r>=p->r时,应当直接返回所记录的子树中的最小值。否则,由于查询区间不完全包含当前区间,所以应该取max(l,p->l)和min(r,p->r)处的自变量在当前节点所对应的函数上的取值,让之参与最小值的比较。(主要是这里想了一天)
其实算法的重点在于,怎样合并两条线段。
case1:如果新线段在当前节点的定义域中,完全小于原来的线段,那应当舍去原来的线段,并且用当前线段的两个端点更新最小值。
case2:新线段在当前节点的定义域中,完全大于原来的线段,应当舍去这条新线段。
case3:新加入的线段和原来的线段在当前的定义域中有交点。求一下两条线段的交点横坐标x。如果新线段的左端点大于旧线段的左端点,或x<=mid,就向左下放;如果新线段的右端点小于旧线段的右交点,或x>mid,就往右下放。
OK.
代码
//树链剖分 #include <cstdio> #include <algorithm> #include <iostream> #define ll long long #define inf 123456789123456789ll #define maxn 210000 #define maxk 17 using namespace std; ll N, M, head[maxn], next[maxn], w[maxn], to[maxn], top[maxn], tid[maxn], tim, dist[maxn], size[maxn], son[maxn], fa[maxn], tot, anc[maxn][maxk+3], deep[maxn], untid[maxn]; struct segtree { ll l, r, min, taga, tagb, dl, dr; segtree *lch, *rch; segtree(){min=inf;lch=rch=0;taga=0;tagb=inf;} }*root; void adde(ll a, ll b, ll v){to[++tot]=b;w[tot]=v;next[tot]=head[a];head[a]=tot;} ll f(ll a, ll b, ll x){return a*x+b;} void addline(segtree *p, ll taga, ll tagb) { ll f1, f2, f3, f4, x, mid; f1=f(p->taga,p->tagb,p->dl),f2=f(p->taga,p->tagb,p->dr); f3=f(taga,tagb,p->dl),f4=f(taga,tagb,p->dr); if(f3<=f1 and f4<=f2) { p->taga=taga,p->tagb=tagb; p->min=min(min(f3,f4),p->min); return; } if(f3>f1 and f4>f2)return; x=(tagb-p->tagb)/(p->taga-taga); mid=p->lch->dr; if(f3<=f1 or x<=mid)addline(p->lch,taga,tagb); if(f4<=f2 or x>mid)addline(p->rch,taga,tagb); p->min=min(p->min,min(p->lch->min,p->rch->min)); } void segtag(segtree *p, ll l, ll r, ll a, ll b) { ll mid=(p->l+p->r)>>1; if(l<=p->l and r>=p->r) { addline(p,a,b); return; } if(l<=mid)segtag(p->lch,l,r,a,b); if(r>mid)segtag(p->rch,l,r,a,b); p->min=min(p->min,min(p->lch->min,p->rch->min)); } ll segmin(segtree *p, ll l, ll r) { ll mid=(p->l+p->r)>>1, ans=inf; if(l<=p->l and r>=p->r)return p->min; if(l<=mid)ans=min(ans,segmin(p->lch,l,r)); if(r>mid)ans=min(ans,segmin(p->rch,l,r)); l=max(l,p->l), r=min(r,p->r); ans=min(ans,f(p->taga,p->tagb,dist[untid[l]])); ans=min(ans,f(p->taga,p->tagb,dist[untid[r]])); return ans; } void build(segtree *p, ll l, ll r) { ll mid=(l+r)>>1; p->l=l,p->r=r; if(l==r){p->dl=p->dr=dist[untid[l]];return;} build(p->lch=new segtree,l,mid); build(p->rch=new segtree,mid+1,r); p->dl=p->lch->dl;p->dr=p->rch->dr; } void dfs1(ll pos) { ll p, v; size[pos]=1; for(p=head[pos];p;p=next[p]) { if((v=to[p])==fa[pos])continue; fa[v]=pos; dist[v]=dist[pos]+w[p]; deep[v]=deep[pos]+1; dfs1(v); if(size[v]>size[son[pos]])son[pos]=v; size[pos]+=size[v]; } } void dfs2(ll pos, ll tp) { ll p, v; top[pos]=tp; tid[pos]=++tim; untid[tid[pos]]=pos; if(son[pos])dfs2(son[pos],tp); for(p=head[pos];p;p=next[p]) if((v=to[p])!=fa[pos] and v!=son[pos])dfs2(v,v); } ll findmin(ll a, ll b) { ll ta=top[a], tb=top[b], ans=inf; while(ta!=tb) { if(deep[ta]<deep[tb])swap(a,b),swap(ta,tb); ans=min(ans,segmin(root,tid[ta],tid[a])); a=fa[ta];ta=top[a]; } if(deep[a]>deep[b])swap(a,b); ans=min(ans,segmin(root,tid[a],tid[b])); return ans; } void maketag(ll a, ll b, ll taga, ll tagb) { ll ta=top[a], tb=top[b]; while(ta!=tb) { if(deep[ta]<deep[tb])swap(a,b),swap(ta,tb); segtag(root,tid[ta],tid[a],taga,tagb); a=fa[ta];ta=top[a]; } if(deep[a]>deep[b])swap(a,b); segtag(root,tid[a],tid[b],taga,tagb); } void calcf() { ll i, k; for(i=1;i<=N;i++)anc[i][0]=fa[i]; for(k=1;k<=maxk;k++) for(i=1;i<=N;i++) anc[i][k]=anc[anc[i][k-1]][k-1]; } ll LCA(ll x, ll y) { ll k; if(deep[x]<deep[y])swap(x,y); for(k=maxk;k>=0;k--)if(deep[anc[x][k]]>=deep[y])x=anc[x][k]; if(x==y)return x; for(k=maxk;k>=0;k--)if(anc[x][k]!=anc[y][k])x=anc[x][k],y=anc[y][k]; return anc[x][0]; } void solve() { ll type, s, t, a, b, i, lca; for(i=1;i<=M;i++) { scanf("%lld%lld%lld",&type,&s,&t); if(type==1) { scanf("%lld%lld",&a,&b); lca=LCA(s,t); maketag(s,lca,-a,a*dist[s]+b); maketag(lca,t,a,a*dist[s]-2*a*dist[lca]+b); } else printf("%lld\n",findmin(s,t)); } } void init() { ll i, a, b, v; scanf("%lld%lld",&N,&M); for(i=1;i<N;i++)scanf("%lld%lld%lld",&a,&b,&v),adde(a,b,v),adde(b,a,v); deep[1]=1; dist[1]=0; dfs1(1); dfs2(1,1); calcf(); build(root=new segtree,1,N); } int main() { init(); solve(); return 0; }
相关文章推荐
- Bzoj4515 [Sdoi2016]游戏
- [树链剖分+李超线段树] BZOJ4515: [Sdoi2016]游戏
- BZOJ4515: [Sdoi2016]游戏
- BZOJ4515: [Sdoi2016]游戏
- [BZOJ4515][Sdoi2016]游戏(树链剖分)
- [树链剖分 李超线段树] BZOJ4515 [Sdoi2016] 游戏
- 4515: [Sdoi2016]游戏
- bzoj4515 [Sdoi2016]游戏(树链剖分+超哥线段树)
- bzoj千题计划276:bzoj4515: [Sdoi2016]游戏
- [bzoj4515][Sdoi2016]游戏-树链剖分+李超线段树
- BZOJ 4515|SDOI 2016|游戏|树链剖分
- 【BZOJ4519】【Sdoi2016】游戏 线段树
- bzoj4515 [Sdoi2016]游戏
- BZOJ 4515 [Sdoi2016]游戏
- bzoj4600 [Sdoi2016]硬币游戏
- bzoj 4515: [Sdoi2016]游戏 树链剖分+线段树
- Bzoj4600--Sdoi2016硬币游戏
- [SDOI 2016]游戏
- 【BZOJ4515】[Sdoi2016]游戏 树链剖分+线段树
- bzoj 4515 [Sdoi2016]游戏 线段树维护凸包