洛谷 P4069 [SDOI2016]游戏 树链剖分+线段树

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82595877

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

输入输出格式

输入格式:
第一行两个数字 n、m,表示树的点数和进行的操作数。
接下来 n−1 行,每行三个数字 u、v、w,表示树上有一条连接 u、v 的边,长度是 w。
接下来 m 行。每行第一个数字是 1 或 2。
若第一个数是 1,表示 Alice 进行操作,接下来四个数字 s、t、a、b。
若第一个数是 2,表示 Bob 进行操作,接下来四个数字 s、t。

输出格式:
每当 Bob 进行操作,输出一行一个数,表示他能够选择的最小的数字

输入输出样例

输入样例#1:
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
输出样例#1:
123456789123456789
6
-106

说明

测试点 1 ~ 2: n 10 m 10 a ∣≤ 10000
测试点 3 ~ 4: n 1000 m 1000 a ∣≤ 10000
测试点 5: n 100000 m 100000 a = 0 ,树是一条链;
测试点 6 ~ 7: n 100000 m 100000 a = 0
测试点 8: n 100000 m 100000 a = 1 ,树是一条链;
测试点 9 ~ 10: n 100000 m 100000 a = 1
测试点 11 ~ 13: n 100000 m 100000 a ∣≤ 10000 ,树是一条链;
测试点 14 ~ 20: n 100000 m 100000 a ∣≤ 10000

分析:
我们考虑一个1操作 ( x , y , A , B ) 的影响。
显然, x l c a 路径上的点 i 多的权值为,

a [ i ] = ( d i s [ x ] d i s [ i ] ) A + B

也就是,
a [ i ] = A d i s [ i ] + ( d i s [ x ] A + B )

l c a y 的路径上点的权值为,
a [ i ] = A d i s [ i ] + ( ( d i s [ x ] 2 d i s [ l c a ] ) A + B )

也就是两个一次函数。
也就是说,我们每次给一条路径上的每个点加一个一次函数,然后查询一条路径上每个点一次函数的最小值的最小值。
树套树不仅不现实,而且很浪费,因为每个点自变量不变。
因为是路径的操作,显然路径上的自变量单调。
算法就很好想了,我们把这些路径拆成 l o g 个区间,并给这些区间加上一条一次函数。而对于查询的每个区间 [ l , r ] ,预处理出 [ l c , r c ] ,表示这个区间的值域。因为只有可能在 l c r c 处取得最小值,类似于二次函数。然后在线段树 [ l , r ] 区间内求出 l c r c 的最小值。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const int maxn=1e5+7;
const LL inf=123456789123456789;

using namespace std;

LL n,m,cnt,x,y,op;
LL ls[maxn],dfn[maxn],id[maxn],top[maxn],size[maxn],fa[maxn],dep[maxn];
LL dis[maxn];
LL w,A,B;

struct line{
    LL k,b;
    LL get(LL x)
    {
        return k*x+b;
    }
};

struct rec{
    LL lc,rc,ans;
    line c;
}t[maxn*4];

struct edge{
    LL y,next;
    LL w;
}g[maxn*2];

void add(LL x,LL y,LL w)
{
    cnt++;
    g[cnt].y=y;
    g[cnt].w=w;
    g[cnt].next=ls[x];
    ls[x]=cnt;
}

void dfs1(LL x,LL f)
{
    size[x]=1;
    fa[x]=f;
    dep[x]=dep[f]+1;
    for (LL i=ls[x];i>0;i=g[i].next)
    {
        LL y=g[i].y;
        if (y==f) continue;
        dis[y]=dis[x]+g[i].w;
        dfs1(y,x); 
        size[x]+=size[y];
    }
}

void dfs2(LL x,LL f)
{
    dfn[x]=++cnt;
    id[cnt]=x;
    top[x]=f;
    LL c=0;
    for (LL i=ls[x];i>0;i=g[i].next)
    {
        LL y=g[i].y;
        if (y==fa[x]) continue;
        if (size[y]>size[c]) c=y;
    }
    if (!c) return;
    dfs2(c,f);
    for (LL i=ls[x];i>0;i=g[i].next)
    {
        LL y=g[i].y;
        if ((y==fa[x]) || (y==c)) continue;
        dfs2(y,y);
    }
}

LL getlca(LL x,LL y)
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]>dep[top[y]]) swap(x,y);
        y=fa[top[y]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    return x;
}

void build(LL p,LL l,LL r)
{
    t[p].c.k=0,t[p].c.b=inf;
    t[p].ans=inf;
    if (l==r)
    {
        t[p].lc=dis[id[l]];
        t[p].rc=dis[id[l]];
        return;
    }
    LL mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    t[p].lc=min(t[p*2].lc,t[p*2+1].lc);
    t[p].rc=max(t[p*2].rc,t[p*2+1].rc);
}

void ins(LL p,LL l,LL r,line d)
{
    if (l==r)
    {
        if (t[p].c.get(t[p].lc)>d.get(t[p].lc)) t[p].c=d;
        t[p].ans=t[p].c.get(t[p].lc);
        return;
    }
    LL mid=(l+r)/2;
    LL lmid=t[p*2].rc;
    if (d.k>t[p].c.k)
    {
        if (d.get(lmid)<t[p].c.get(lmid))
        {
            ins(p*2+1,mid+1,r,t[p].c);
            t[p].c=d;
        }
        else ins(p*2,l,mid,d);
    }
    else
    {
        if (d.get(lmid)<t[p].c.get(lmid))
        {
            ins(p*2,l,mid,t[p].c);
            t[p].c=d;
        }
        else ins(p*2+1,mid+1,r,d);
    }
    LL tmp=min(t[p].c.get(t[p].lc),t[p].c.get(t[p].rc));
    t[p].ans=min(min(t[p*2].ans,t[p*2+1].ans),tmp);
}

void change(LL p,LL l,LL r,LL x,LL y,line d)
{
    if ((l==x) && (r==y))
    {
        ins(p,l,r,d);
        return;
    }
    LL mid=(l+r)/2;
    if (y<=mid) change(p*2,l,mid,x,y,d);
    else if (x>mid) change(p*2+1,mid+1,r,x,y,d);
    else
    {
        change(p*2,l,mid,x,mid,d);
        change(p*2+1,mid+1,r,mid+1,y,d);
    }
    LL tmp=min(t[p].c.get(t[p].lc),t[p].c.get(t[p].rc));
    t[p].ans=min(min(t[p*2].ans,t[p*2+1].ans),tmp);
}

void inslink(LL x,LL y,LL k,LL b)
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]>dep[top[y]]) swap(x,y);
        change(1,1,n,dfn[top[y]],dfn[y],(line){k,b});
        y=fa[top[y]];
    }
    change(1,1,n,dfn[x],dfn[y],(line){k,b});
}

LL query(LL p,LL l,LL r,LL x,LL y,LL &lc,LL &rc)
{
    if ((l==x) && (r==y))
    {
        lc=min(lc,t[p].lc);
        rc=max(rc,t[p].rc);
        return t[p].ans;
    }
    LL mid=(l+r)/2;
    LL tmp;
    if (y<=mid) tmp=query(p*2,l,mid,x,y,lc,rc);
    else if (x>mid) tmp=query(p*2+1,mid+1,r,x,y,lc,rc);
    else
    {
        LL lc1=inf,lc2=inf,rc1=0,rc2=0;
        tmp=min(query(p*2,l,mid,x,mid,lc1,rc1),query(p*2+1,mid+1,r,mid+1,y,lc2,rc2));
        lc=min(lc1,lc2);
        rc=max(rc1,rc2);
    }
    tmp=min(tmp,min(t[p].c.get(lc),t[p].c.get(rc)));
    return tmp;
}

LL getans(LL x,LL y)
{
    LL ans=inf,lc,rc;
    while (top[x]!=top[y])
    {
        if (dep[top[x]]>dep[top[y]]) swap(x,y);
        lc=inf,rc=0;
        ans=min(ans,query(1,1,n,dfn[top[y]],dfn[y],lc,rc));
        y=fa[top[y]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    lc=inf,rc=0;
    ans=min(ans,query(1,1,n,dfn[x],dfn[y],lc,rc));
    return ans;
}

int main()
{
    scanf("%lld%lld",&n,&m);
    for (LL i=1;i<n;i++)
    {
        scanf("%lld%lld%lld",&x,&y,&w);
        add(x,y,w);
        add(y,x,w); 
    }
    dfs1(1,0);
    cnt=0;
    dfs2(1,0);
    build(1,1,n);   
    for (LL i=1;i<=m;i++)
    {
        scanf("%lld",&op);
        if (op==1)
        {
            scanf("%lld%lld%lld%lld",&x,&y,&A,&B);
            LL lca=getlca(x,y);
            inslink(lca,x,-A,dis[x]*A+B);
            inslink(lca,y,A,(dis[x]-2*dis[lca])*A+B);
        }
        else
        {               

            scanf("%lld%lld",&x,&y);            
            printf("%lld\n",getans(x,y));
        }
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/82595877
今日推荐