[BZOJ]4515: [Sdoi2016]游戏 树链剖分+李超线段树

Description

Alice 和 Bob 在玩一个游戏。
游戏在一棵有 n 个点的树上进行。最初,每个点上都只有一个数字,那个数字是 123456789123456789。
有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字。对于路径上的一个点 r,若 r 与 s 的距离是 dis,那么 Alice 在点 r 上添加的数字是 a×dis+b。有时,Bob 会选择一条从 s 到 t 的路径。
他需要先从这条路径上选择一个点,再从那个点上选择一个数字。
Bob 选择的数字越小越好,但大量的数字让 Bob 眼花缭乱。Bob 需要你帮他找出他能够选择的最小的数字。

Solution

显然加点实际上就是加入一条直线,而且这条直线是关于 d i s dis dis的,那么就有一个问题,用李超线段树维护的时候需要一段区间值是从小到大的,但是仔细想想,这个条件其实是满足的,就没有问题。
然后就是一些李超线段树的基本操作了,然而我太强,板子太久没打,全靠自己YY,一堆错……

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,dep[Maxn],fa[Maxn][17],son[Maxn],sz[Maxn],top[Maxn],dfn[Maxn],DFN=0,to[Maxn];LL dis[Maxn];
struct Edge{int y,d,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y,int d)
{
    int t=++len;
    e[t].y=y;e[t].d=d;e[t].next=last[x];last[x]=t;
}
void dfs1(int x,int ff)
{
    dep[x]=dep[ff]+1,fa[x][0]=ff,sz[x]=1;
    for(int i=1;(1<<i)<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(y==ff)continue;
        dis[y]=dis[x]+e[i].d;dfs1(y,x);sz[x]+=sz[y];
        if(sz[y]>sz[son[x]])son[x]=y;
    }
}
void dfs2(int x,int Top)
{
    top[x]=Top,dfn[x]=++DFN;to[DFN]=x;
    if(son[x])dfs2(son[x],Top);
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(y==fa[x][0]||y==son[x])continue;
        dfs2(y,y);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=16;i>=0;i--)
    if((1<<i)<=dep[x]-dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=16;i>=0;i--)
    if((1<<i)<=dep[x]&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
struct Line{LL k,b;}L[Maxn<<1];int ll=0;
LL get(LL x,int id){if(id==-1)return 123456789123456789LL;return L[id].k*x+L[id].b;}
struct Seg{int l,r,lc,rc,id;LL mn;}tr[Maxn<<1];
int tot=0;
void up(int x)
{
    int lc=tr[x].lc,rc=tr[x].rc;
    LL pl=get(dis[to[tr[x].l]],tr[x].id),pr=get(dis[to[tr[x].r]],tr[x].id);
    tr[x].mn=min(min(pl,pr),min(tr[lc].mn,tr[rc].mn));
}
void build(int l,int r)
{
    int x=++tot;
    tr[x].l=l;tr[x].r=r;tr[x].id=-1;tr[x].mn=123456789123456789LL;
    if(l==r)return;
    int mid=l+r>>1;
    tr[x].lc=tot+1,build(l,mid);
    tr[x].rc=tot+1,build(mid+1,r);
}
void insert(int x,int id)
{
    LL nl=get(dis[to[tr[x].l]],id),nr=get(dis[to[tr[x].r]],id);
    if(tr[x].id==-1){tr[x].id=id;up(x);return;}
    LL pl=get(dis[to[tr[x].l]],tr[x].id),pr=get(dis[to[tr[x].r]],tr[x].id); 
    if(nl<=pl&&nr<=pr){tr[x].id=id;up(x);return;}
	if(nl>=pl&&nr>=pr)return;
	if(tr[x].l==tr[x].r)return;
    int mid=tr[x].l+tr[x].r>>1;
    LL pt=get(dis[to[mid]],tr[x].id),nt=get(dis[to[mid]],id);
    if(nt<=pt)
    {
    	if(pr<=nr)insert(tr[x].rc,tr[x].id);
    	else insert(tr[x].lc,tr[x].id);
        tr[x].id=id;
    }
    else
    {
    	if(nr<=pr)insert(tr[x].rc,id);
    	else insert(tr[x].lc,id);
    }
    up(x);
}
void work(int x,int l,int r,int id)
{
	if(tr[x].l==l&&tr[x].r==r){insert(x,id);return;}
	int lc=tr[x].lc,rc=tr[x].rc,mid=tr[x].l+tr[x].r>>1;
	if(r<=mid)work(lc,l,r,id);
	else if(l>mid)work(rc,l,r,id);
	else work(lc,l,mid,id),work(rc,mid+1,r,id);
	up(x);
}
struct Node{int l,r;LL v;Node(int _l=0,int _r=0,LL _v=0){l=_l,r=_r,v=_v;}};
Node query(int x,int l,int r)
{
	LL re=min(get(dis[to[tr[x].l]],tr[x].id),get(dis[to[tr[x].r]],tr[x].id));
    if(tr[x].l==l&&tr[x].r==r)return Node(l,r,min(re,tr[x].mn));
    Node t,t1,t2;
    int lc=tr[x].lc,rc=tr[x].rc,mid=tr[x].l+tr[x].r>>1;
    if(r<=mid)
	{
		t=query(lc,l,r);
		re=min(get(dis[to[t.l]],tr[x].id),get(dis[to[t.r]],tr[x].id));
	}
    else if(l>mid)
	{
		t=query(rc,l,r);
		re=min(get(dis[to[t.l]],tr[x].id),get(dis[to[t.r]],tr[x].id));
	}
    else
    {
    	t1=query(lc,l,mid),t2=query(rc,mid+1,r);
    	t.v=min(t1.v,t2.v);t.l=t1.l,t.r=t2.r;
    	re=min(get(dis[to[t.l]],tr[x].id),get(dis[to[t.r]],tr[x].id));
	}
	t.v=min(t.v,re);
	return t;
}
void Insert(int x,int y,LL k,LL b)
{
    int tx=top[x],ty=top[y];L[++ll].k=k,L[ll].b=b;
    while(tx!=ty)
    {
        if(dep[tx]<dep[ty])swap(tx,ty),swap(x,y);
        work(1,dfn[tx],dfn[x],ll);
        x=fa[tx][0],tx=top[x];
    }
    if(dep[x]>dep[y])swap(x,y);
    work(1,dfn[x],dfn[y],ll);
}
LL Query(int x,int y)
{
    int tx=top[x],ty=top[y];LL re=123456789123456789LL;
    while(tx!=ty)
    {
        if(dep[tx]<dep[ty])swap(tx,ty),swap(x,y);
        re=min(re,query(1,dfn[tx],dfn[x]).v);
        x=fa[tx][0],tx=top[x];
    }
    if(dep[x]>dep[y])swap(x,y);
    return min(re,query(1,dfn[x],dfn[y]).v);
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read(),d=read();
        ins(x,y,d),ins(y,x,d);
    }
    dis[0]=0,dep[0]=-1;dfs1(1,0);dfs2(1,1);
	tr[0].mn=123456789123456789LL;
    build(1,n);
    while(m--)
    {
        int op=read(),s=read(),t=read();LL k,b;
        if(op==1)
        {
            k=read(),b=read();int p=LCA(s,t);
            Insert(s,p,-k,b+k*dis[s]);
            Insert(t,p,k,b+k*dis[s]-2LL*k*dis[p]);
        }
        else printf("%lld\n",Query(s,t));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值