您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: