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=,f=; char ch=getchar();
while (ch<'' || ch>'') {if (ch=='-')f=-; ch=getchar();}
while (ch>='' && ch<='') {x=x*+ch-''; ch=getchar();}
return x*f;
}
#define maxn 100100
struct EdgeNode{int next,to;long long len;}edge[maxn<<];
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]=;
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]+;
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<<];
#define inf 123456789123456789LL
void Build(int now,int l,int r)
{
tree[now].a=; tree[now].b=tree[now].minn=inf;
if (l==r) return;
int mid=(l+r)>>;
Build(now<<,l,mid); Build(now<<|,mid+,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)>>;
if (R<=mid) return min(re,Query(now<<,l,mid,L,R));
else if (L>mid) return min(re,Query(now<<|,mid+,r,L,R));
else return min(re,min(Query(now<<,l,mid,L,mid),Query(now<<|,mid+,r,mid+,R)));
}
void Change(int now,int l,int r,long long a,long long b)
{
int mid=(l+r)>>,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<<,l,mid,tree[now].a,tree[now].b);
else Change(now<<|,mid+,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<<,l,mid,a,b);
else Change(now<<|,mid+,r,a,b);
tree[now].minn=min(tree[now].minn,min(tree[now<<].minn,tree[now<<|].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)>>;
if (L<=mid) change(now<<,l,mid,L,R,a,b);
if (R>mid) change(now<<|,mid+,r,L,R,a,b);
tree[now].minn=min(tree[now].minn,min(tree[now<<].minn,tree[now<<|].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(,,n,pl[top[x]],pl[x],-a,a*dis[s]+b);
x=fa[top[x]];
}
change(,,n,pl[lca],pl[x],-a,a*dis[s]+b);
while (top[y]!=top[lca])
{
change(,,n,pl[top[y]],pl[y],a,dis[s]*a-dis[lca]**a+b);
y=fa[top[y]];
}
if (y!=lca) change(,,n,pl[lca]+,pl[y],a,dis[s]*a-dis[lca]**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(,,n,pl[top[x]],pl[x]),re);
x=fa[top[x]];
}
if (deep[x]>deep[y]) swap(x,y);
re=min(Query(,,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=; i<=n-; i++)
u=read(),v=read(),w=(long long)read(),insert(u,v,w);
dfs_1(); dfs_2(,); Build(,,n);
while (m--)
{
int opt=read();
if (opt==)
{
int s=read(),t=read();long long a=read(),b=read();
Solve_Insert(s,t,a,b);
}
if (opt==)
{
int s=read(),t=read();
printf("%lld\n",Solve_Query(s,t));
}
}
return ;
}

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

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

  1. 【xsy3423】党² 线段树+李超线段树or动态半平面交

    本来并不打算出原创题的,此题集CF542A和sk的灵感而成,算个半原创吧. 题目大意: 给定有$n$个元素的集合$P$,其中第$i$个元素中包含$L_i,R_i,V_i$三个值. 给定另一个有$n$个 ...

  2. bzoj 4445 小凸想跑步 - 半平面交

    题目传送门 vjudge的快速通道 bzoj的快速通道 题目大意 问在一个凸多边形内找一个点,连接这个点和所有顶点,使得与0号顶点,1号顶点构成的三角形是最小的概率. 假设点的位置是$(x, y)$, ...

  3. BZOJ 4445 [Scoi2015]小凸想跑步:半平面交

    传送门 题意 小凸晚上喜欢到操场跑步,今天他跑完两圈之后,他玩起了这样一个游戏. 操场是个凸 $ n $ 边形,$ n $ 个顶点 $ P_i $ 按照逆时针从 $ 0 $ 至 $ n-1 $ 编号. ...

  4. BZOJ.3938.Robot(李超线段树)

    BZOJ UOJ 以时间\(t\)为横坐标,位置\(p\)为纵坐标建坐标系,那每个机器人就是一条\(0\sim INF\)的折线. 用李超线段树维护最大最小值.对于折线分成若干条线段依次插入即可. 最 ...

  5. 洛谷P4069 [SDOI2016]游戏(李超线段树)

    题意 题目链接 Sol 这题细节好多啊qwq..稍不留神写出一个小bug就要调1h+.. 思路就不多说了,把询问区间拆成两段就是李超线段树板子题了. 关于dis的问题可以直接维护. // luogu- ...

  6. 线段树&数链剖分

    傻逼线段树,傻逼数剖 线段树 定义: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 使用线段树可以快速的查找某一个节点在若干条线段中出现 ...

  7. bzoj 4515: 游戏 树链剖分+线段树

    题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=4515 题解: 先让我%一发lych大佬点我去看dalao的题解 讲的很详细. 这里纠正一 ...

  8. bzoj 2732 射箭 半平面交

    2732: [HNOI2012]射箭 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2531  Solved: 848[Submit][Status] ...

  9. BZOJ 1038 ZJOI2008 瞭望塔 半平面交

    题目大意及模拟退火题解:见 http://blog.csdn.net/popoqqq/article/details/39340759 这次用半平面交写了一遍--求出半平面交之后.枚举原图和半平面交的 ...

随机推荐

  1. C语言: 创建数组的几种方法

    创建数组有三种方法 1.声明一个数组,声明时用常量表达式指定数组维数,然后可以用数组名访问数组元素 2.声明一个变长数组,声明时用变量表达式指定数组的维数,C99支持 3.声明一个指针,调用mallo ...

  2. php的一些小细节

    1.今天看见 $arr3 = array_filter($arr, create_function('$v', 'return strlen($v);')); 作用就是去掉为空的元素,其实当callb ...

  3. Twitter Snowflake 的Java实现

    在关闭显示的情况下, 可以达到每毫秒3万个的生成速度 /** * An Implementation of Twitter Snowflake ID Generator */ public class ...

  4. Effective java 第2版 - 笔记(01) 单例(Singleton)的枚举(enum)实现

    直接上代码: public enum Boss { INSTANCE; private String name; public void doSomeThing() { System.out.prin ...

  5. java中String、StringBuffer、StringBuilder的区别

    java中String.StringBuffer.StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题.现在总结一下,看看他们的不同与相同. 1.可变与不可 ...

  6. infer.net 入门2 用一个侦探故事来讲解,通俗易懂

    The results look OK, but how do you know that you aren’t missing something. Would a more sophisticat ...

  7. 最佳实践 —— 详细谈谈如何减小APK体积

    转载请注明出处: http://www.cnblogs.com/soaringEveryday/p/5254520.html 随着Android移动开发的需求越来越复杂,我们不可避免的遇到发布出去的a ...

  8. 20160223 - Windows 10 的文件资源管理器下出现重复文件夹的解决办法

    现象: 安装 OneDrive 从 Windows 7.8.8.1 升级来的 Windows 10 的电脑,可能会出现文件资源管理器左侧面板中出现重复的文件夹. 通常有:视频.图片.文档.下载.音频. ...

  9. android中的图片处理

    大图片处理 大图片处理是将原来像素高的转换为像素低的图片,比如原来图片是1024*768的,而手机屏幕是800*600的,这时候就需要进行转换.转换的方式很简单就是等比例缩放. package xid ...

  10. 如何设置div高度为100%

    div高度通常都是固定值,直接将div高度设为100%是无效的,那么如何设置才能有效呢? 直接给div设置height:100%即可,无效的原因一定是父元素的高度为0,最常见的就是给body的直接元素 ...