【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\n",Solve_Query(s,t));
                }
        }
    return 0;
}

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

时间: 2024-08-26 16:32:49

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

BZOJ 1038 ZJOI 2008 瞭望塔 半平面交

题目大意:给出一个村庄的轮廓,在这个村庄里可以在随意的地方建一个瞭望塔.这个塔须要足够高,使得可以看得村庄的全貌. 求这个瞭望塔的最小高度. 思路:对于村庄中的每一条边,瞭望塔为了看见它.必需要在这个直线左側的半平面区域.这种话为了满足全部的边的需求,做一次半平面交,瞭望塔的最高点必须在全部边的半平面交的区域内. 例如以下图例子. 全部边的半平面交区域就是上面的图形.设上面半平面的函数关系是F(x).村长的函数关系是G(x),那么问题就转化成了求一个x,使得(F(x) - G(x))最小. 这个

bzoj 2732 射箭 半平面交

2732: [HNOI2012]射箭 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2531  Solved: 848[Submit][Status][Discuss] Description 沫沫最近在玩一个二维的射箭游戏,如下图 1 所示,这个游戏中的 x 轴在地面,第一象限中有一些竖直线段作为靶子,任意两个靶子都没有公共部分,也不会接触坐标轴.沫沫控制一个位于(0,0)的弓箭手,可以朝 0 至 90?中的任意角度(不包括 0度和 90度),

BZOJ 2732 HNOI 2012 射箭 半平面交

题目大意:给出一些与x轴垂直的线段,问一个经过原点的抛物线最多能按顺序经过多少条线段. 思路:总体上来说是数学题,我们来推一推. 设这个经过原点的抛物线为y = a * x ^ 2 + b * x,设一条线段的起点和终点为(x0,y1)和(x0,y2),且y2 > y1. 将x0带入到设出的抛物线中,会得到y = a * x0 ^ 2 + b * x0,这时候需要满足的是y <= y2 && y >= y1,也就是a * x0 ^ 2 + b * x0 <= y2

BZOJ 3800 Saber VS Lancer/POJ 1755 Triathlon 半平面交

题目大意:有n个人进行铁人三项比赛,对于这三种运动,每个人都有一个固定的速度,但是每种运动的长度是多少并不知道.现在问裁判可不可以通过调整这三项运动的长度来使某一个人赢得比赛. 思路:考虑现在我们想让一个人赢的时候,这个人的三个速度分别为v1,v2,v3,想让所有人都输给他,设某一个人的三个速度是v1',v2',v3'.设三项的比赛的长度为l1,l2,l3.那么不难得到如下方程:l1 / v1 + l2 / v2 + l3 / v3 < l1 / v1' + l2 / v2' + l3 / v3

BZOJ 1137: [POI2009]Wsp 岛屿 半平面交

1137: [POI2009]Wsp 岛屿 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 165  Solved: 78[Submit][Status][Discuss] Description Byteotia岛屿是一个凸多边形.城市全都在海岸上.按顺时针编号1到n.任意两个城市之间都有一条笔直的道路相连.道路相交处可以自由穿行.有一些道路被游击队控制了,不能走,但是可以经过这条道路与未被控制的道路的交点.问

BZOJ 2618 CQOI2006 凸多边形 半平面交

题目大意:给定n个凸多边形,求交集的面积 时隔多年我终于把完整的半平面交搞出来了--真尼玛艰辛-- 曾经写了一发 RE到死 于是就搁置0.0 今天写一发又是WA到死的节奏-- 不多说直接上代码 其实刘汝佳同学写麻烦了 每次插入一个半平面之后不用两端都删的 只删一端 最后再处理两端的部分就行 300题留念--切了道模板题也不错 #include <cmath> #include <cstdio> #include <cstring> #include <iostre

BZOJ 1038 ZJOI2008 瞭望塔 半平面交

题目大意及模拟退火题解:见 http://blog.csdn.net/popoqqq/article/details/39340759 这次用半平面交写了一遍--求出半平面交之后.枚举原图和半平面交的每一个点,求出答案就可以 #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 310 #define

BZOJ 2618 CQOI 2006 凸多边形 半平面交

题目大意:给出n个凸多边形,求这些多边形的面积的交. 思路:犯傻了..以后看到凸多边形第一时间就要想到半平面交啊..多明显啊,半天愣着没想出来. CODE: #include <cmath> #include <cstdio> #include <iomanip> #include <cstring> #include <iostream> #include <algorithm> #define MAX 6100 #define E

BZOJ 1137 半平面交

半平面交的板子 //By SiriusRen #include <bits/stdc++.h> #define double long double using namespace std; const int N=100050; const double eps=1e-10; int n,m,xx,yy,tot; double Ans; vector<int>vec[N]; struct Point{double x,y;}point[N]; struct Line{Point