【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交

4515: [Sdoi2016]游戏

Time Limit: 40 Sec  Memory Limit: 256 MB
Submit: 304  Solved: 129
[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

鸣谢Menci上传

Solution

仍旧是李超线段树维护半平面交,唯一不同的是这里有了一个距离的定义

只需要在比较的时候带入端点和终点的距离即可,至于李超线段树,此处安利一个非常好的文章:戳这里

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
long long read()
{
    long long 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*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define maxn 100100
struct EdgeNode{int next,to;long long len;}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v,long long w)
{
    cnt++;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt;edge[cnt].len=w;
}
void insert(int u,int v,long long w) {add(u,v,w); add(v,u,w);}
int n,m;
//--------------------------------------------------------------------------------------------------------
int pl[maxn],sz,pr[maxn],size[maxn],deep[maxn],son[maxn],top[maxn],fa[maxn];long long dis[maxn],pre[maxn];
void dfs_1(int x)
{
    size[x]=1;
    for (int i=head[x]; i; i=edge[i].next) 
        if (edge[i].to!=fa[x])
            {
                fa[edge[i].to]=x;
                deep[edge[i].to]=deep[x]+1;
                dis[edge[i].to]=dis[x]+edge[i].len;
                dfs_1(edge[i].to);
                if (size[son[x]]<size[edge[i].to]) son[x]=edge[i].to;
                size[x]+=size[edge[i].to];
            }
}
void dfs_2(int x,int chain)
{
    pl[x]=++sz; pre[sz]=dis[x]; top[x]=chain; 
    if (son[x]) dfs_2(son[x],chain);
    for (int i=head[x]; i; i=edge[i].next)
        if (edge[i].to!=fa[x] && edge[i].to!=son[x])
            dfs_2(edge[i].to,edge[i].to);
    pr[x]=sz;
}
int LCA(int x,int y)
{
    while (top[x]!=top[y])
    {
        if (deep[top[x]]<deep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }   
    if (deep[x]>deep[y]) swap(x,y);
    return x;
}
//--------------------------------------------------------------------------------------------------------
long long f(long long x,long long k,long long b) {return k*x+b;}
struct TreeNode{long long a,b,minn;}tree[maxn<<2];
#define inf 123456789123456789LL
void Build(int now,int l,int r)
{
    tree[now].a=0; tree[now].b=tree[now].minn=inf;
    if (l==r) return;
    int mid=(l+r)>>1;
    Build(now<<1,l,mid); Build(now<<1|1,mid+1,r);
} 
long long Query(int now,int l,int r,int L,int R)
{
    long long re=min(f(pre[L],tree[now].a,tree[now].b),f(pre[R],tree[now].a,tree[now].b));
    if (l==L && r==R) return min(re,tree[now].minn);
    int mid=(l+r)>>1; 
    if (R<=mid) return min(re,Query(now<<1,l,mid,L,R)); 
        else if (L>mid) return min(re,Query(now<<1|1,mid+1,r,L,R)); 
            else return min(re,min(Query(now<<1,l,mid,L,mid),Query(now<<1|1,mid+1,r,mid+1,R)));
}
void Change(int now,int l,int r,long long a,long long b)
{
    int mid=(l+r)>>1,fl,fr,fm;
    fl=(f(pre[l],tree[now].a,tree[now].b)>f(pre[l],a,b));
    fr=(f(pre[r],tree[now].a,tree[now].b)>f(pre[r],a,b));
    fm=(f(pre[mid],tree[now].a,tree[now].b)>f(pre[mid],a,b));
    if (fl&fr&fm)
        {
            tree[now].a=a;tree[now].b=b;tree[now].minn=min(tree[now].minn,min(f(pre[l],a,b),f(pre[r],a,b)));
            return;
        }
    if (!(fl|fr|fm)) return;
    if (fm)
        {
            if (fr) Change(now<<1,l,mid,tree[now].a,tree[now].b);
                else Change(now<<1|1,mid+1,r,tree[now].a,tree[now].b);
            tree[now].a=a;tree[now].b=b;tree[now].minn=min(tree[now].minn,min(f(pre[l],a,b),f(pre[r],a,b)));
        }
    if (!fm)
        if (!fr) Change(now<<1,l,mid,a,b);
            else Change(now<<1|1,mid+1,r,a,b);
    tree[now].minn=min(tree[now].minn,min(tree[now<<1].minn,tree[now<<1|1].minn));
}
void change(int now,int l,int r,int L,int R,long long a,long long b)
{
    if (L<=l && R>=r) {Change(now,l,r,a,b); return;}
    int mid=(l+r)>>1;
    if (L<=mid) change(now<<1,l,mid,L,R,a,b); 
    if (R>mid) change(now<<1|1,mid+1,r,L,R,a,b);
    tree[now].minn=min(tree[now].minn,min(tree[now<<1].minn,tree[now<<1|1].minn));
}
//--------------------------------------------------------------------------------------------------------
void Solve_Insert(int s,int t,long long a,long long b)
{
    int lca=LCA(s,t); int x=s,y=t;
    while (top[x]!=top[lca])
        {
            change(1,1,n,pl[top[x]],pl[x],-a,a*dis[s]+b);
            x=fa[top[x]];
        }
    change(1,1,n,pl[lca],pl[x],-a,a*dis[s]+b);
    while (top[y]!=top[lca])
        {
            change(1,1,n,pl[top[y]],pl[y],a,dis[s]*a-dis[lca]*2*a+b);
            y=fa[top[y]];
        }
    if (y!=lca) change(1,1,n,pl[lca]+1,pl[y],a,dis[s]*a-dis[lca]*2*a+b);
}
long long Solve_Query(int s,int t)
{
    int x=s,y=t; long long re=inf;
    while (top[x]!=top[y])
        {
            if (deep[top[x]]<deep[top[y]]) swap(x,y);
            re=min(Query(1,1,n,pl[top[x]],pl[x]),re);
            x=fa[top[x]];
        }
    if (deep[x]>deep[y]) swap(x,y);
    re=min(Query(1,1,n,pl[x],pl[y]),re);
    return re;
}
//--------------------------------------------------------------------------------------------------------
int main()
{
//    freopen("menci_game.in","r",stdin);
//    freopen("menci_game.out","w",stdout);
    n=read();m=read(); long long w;
    for (int u,v,i=1; i<=n-1; i++)
        u=read(),v=read(),w=(long long)read(),insert(u,v,w);
    dfs_1(1); dfs_2(1,1); Build(1,1,n);
    while (m--)
        {
            int opt=read();
            if (opt==1)
                {
                    int s=read(),t=read();long long a=read(),b=read();
                    Solve_Insert(s,t,a,b);
                }
            if (opt==2)
                {
                    int s=read(),t=read();
                    printf("%lld
",Solve_Query(s,t));
                }
        }
    return 0;
}

考场上,看到这个题,这不是裸树链剖分么,线段树维护半平面交,裸李超线段树啊,Clrs的模版上有哎,虽然我没写过,但是我知道大体的方法啊,然后开始码,码到最后连暴力都没打,然后愉快滚粗TAT/...

原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5487049.html