bzoj 4515: [Sdoi2016]游戏
Description
Input
Output
每当 Bob 进行操作,输出一行一个数,表示他能够选择的最小的数字
Sample Input
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
6
-106
HINT
n≤100000,m≤100000,∣a∣≤10000,0<=w,|b|<=10^9
Source
SDOI惊现天天爱跑步的套路,首先a*dis+b,拆路径来讨论;
对于s-lca,变为a*(d[s]-d[i])+b,也就是-a*d[i]+a*d[s]+b;
对于lca-t,变为a*(d[s]+d[i]-2*d[lca]),也就是a*d[i]+a*(d[s]-2*d[lca])+b;
所以每个点的d[i],就相当于是一个横坐标,然后对于这个点插入一个kx+b的直线;
因为树链剖分的log个区间中每个区间的d[i]是递增的;
那么就相当于对这一个区间插入了一条kx+b的直线;然后我们需要动态的维护每个节点的最小值;
那么因为横坐标都是整点,我们可以用线段树来维护这一个半平面交;
用一种比较特殊的标记永久化方法;标记在这个区间中的最小值在哪一条直线上;
然后区间插入直线的时候算出交点,然后分情况递归更改左右区间即可;具体做法看lych即可,太强了;
以下为蒯的:
不妨考虑标记永久化(这里只是一定程度上的永久化但还是要下传的)。
让线段树中的一个节点只对应一条直线,那么如果在这个区间加入一条直线怎么办呢?
要分类讨论,设新加入的f1(x)=k1x+b1,原来的f2(x)=k2x+b2,左端点为l,右端点为r,那么有:
1.f1(d[l])<f2(d[l])且f1(d[r])<f2(d[r]),对应一条直线在两个端点都比另一条小,那么显然在l~r中f1(x)处处比f2(x)小,直接把f2(x)替换为f1(x);
2.同理若上式的两个符号都为>,那么f1(x)处处不如f2(x)优,不做更改。
3.k1<k2,那么由于不满足1.2,显然两条直线有交点,此时解不等式f1(x)<f2(x)得到x>(b1-b2)/(k2-k1),那么判断(b1-b2)/(k2-k1)在左半区间还是右半区间递归下传即可;
4.k1>k2同理。
实际上这就是线段树维护半平面交的过程~~~~~
询问就简单多了,直接用标记永久化的线段树的方法更新即可。
// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#define lson x<<1
#define rson x<<1|1
#define int long long
using namespace std;
typedef long long ll;
const int N=400050;
const ll Inf=123456789123456789ll;
int head[N],to[N],nxt[N],v[N],d[N],deep[N],cnt,n,m,tot;
int size[N],dfn[N],id[N],tt,son[N],top[N],fa[N];
ll Min[N*4],lazy[N*4],k[N*4],b[N*4],ans;
void lnk(int x,int y,int z){
to[++cnt]=y,nxt[cnt]=head[x],v[cnt]=z,head[x]=cnt;
to[++cnt]=x,nxt[cnt]=head[y],v[cnt]=z,head[y]=cnt;
}
void dfs1(int x,int f){
size[x]=1;deep[x]=deep[f]+1;
for(int i=head[x];i;i=nxt[i]){
int y=to[i];if(y==f) continue;
d[y]=d[x]+v[i];dfs1(y,x);
fa[y]=x;size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
void dfs2(int x,int f){
top[x]=f;dfn[x]=++tt;id[tt]=x;
if(son[x]) dfs2(son[x],f);
for(int i=head[x];i;i=nxt[i]){
int y=to[i];if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
struct data{
int l,r,fl;
}p[N];
int lca(int x,int y){
tot=0;int fl=1;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y),fl^=1;
p[++tot]=(data){dfn[top[x]],dfn[x],fl};
x=fa[top[x]];
}
if(deep[x]<deep[y]) swap(x,y),fl^=1;
p[++tot]=(data){dfn[y],dfn[x],fl};
return y;
}
void pushup(int x,int l,int r){
if(l<r) Min[x]=min(Min[lson],Min[rson]);else Min[x]=Inf;
if(lazy[x]){
Min[x]=min(Min[x],min(k[x]*d[id[l]],k[x]*d[id[r]])+b[x]);
}
}
void build(int x,int l,int r){
if(l==r){Min[x]=Inf;return;}
int mid=(l+r)>>1;
build(lson,l,mid);build(rson,mid+1,r);
pushup(x,l,r);
}
void update(int x,int l,int r,ll K,ll B){
if(!lazy[x]){
lazy[x]=1;k[x]=K,b[x]=B;pushup(x,l,r);return;
}
ll x1=d[id[l]]*K+B,y1=d[id[r]]*K+B,x2=d[id[l]]*k[x]+b[x],y2=d[id[r]]*k[x]+b[x];
if(x1<=x2&&y1<=y2){
k[x]=K,b[x]=B;pushup(x,l,r);return;
}
else if(x2<=x1&&y2<=y1){return;}
else if(K<k[x]){
int mid=(l+r)>>1;
int fj=(B-b[x])/(k[x]-K)+1;
if(fj<=d[id[mid]]){
swap(k[x],K);swap(b[x],B);
update(lson,l,mid,K,B);
}
else update(rson,mid+1,r,K,B);
pushup(x,l,r);
}
else if(K>k[x]){
int mid=(l+r)>>1;
int fj=(b[x]-B-1)/(K-k[x]);
if(fj>d[id[mid]]){
swap(k[x],K),swap(b[x],B);
update(rson,mid+1,r,K,B);
}
else update(lson,l,mid,K,B);
pushup(x,l,r);
}
}
void insert(int x,int l,int r,int xl,int xr,ll K,ll B){
if(xl<=l&&r<=xr){update(x,l,r,K,B);return;}
int mid=(l+r)>>1;
if(xr<=mid) insert(lson,l,mid,xl,xr,K,B);
else if(xl>mid) insert(rson,mid+1,r,xl,xr,K,B);
else insert(lson,l,mid,xl,mid,K,B),insert(rson,mid+1,r,mid+1,xr,K,B);
pushup(x,l,r);
}
void query(int x,int l,int r,int xl,int xr){
if(xl<=l&&r<=xr) {ans=min(ans,Min[x]);return;}
int mid=(l+r)>>1;
if(lazy[x]) ans=min(ans,min(k[x]*d[id[xl]],k[x]*d[id[xr]])+b[x]);
if(xr<=mid) query(lson,l,mid,xl,xr);
else if(xl>mid) query(rson,mid+1,r,xl,xr);
else query(lson,l,mid,xl,mid),query(rson,mid+1,r,mid+1,xr);
}
main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<n;i++){int x,y,z;scanf("%lld%lld%lld",&x,&y,&z);lnk(x,y,z);}
dfs1(1,0);dfs2(1,1);build(1,1,n);
for(int i=1;i<=m;i++){
int type;scanf("%lld",&type);
if(type==1){
int s,t,a,b;scanf("%lld%lld%lld%lld",&s,&t,&a,&b);
int Lca=lca(s,t);
ll k=a,b1=a*d[s]+b,b2=a*(d[s]-2*d[Lca])+b;
for(int j=1;j<=tot;j++){
if(p[j].fl==1) insert(1,1,n,p[j].l,p[j].r,-k,b1);
else insert(1,1,n,p[j].l,p[j].r,k,b2);
}
}
else{
ans=Inf;int s,t;scanf("%lld%lld",&s,&t);
int Lca=lca(s,t);
for(int j=1;j<=tot;j++) query(1,1,n,p[j].l,p[j].r);
printf("%lld\n",ans);
}
}
return 0;
}
bzoj 4515: [Sdoi2016]游戏的更多相关文章
- BZOJ.4515.[SDOI2016]游戏(树链剖分 李超线段树)
BZOJ 洛谷 每次在路径上加的数是个一次函数,容易看出是树剖+李超线段树维护函数最小值.所以其实依旧是模板题. 横坐标自然是取个确定的距离标准.取每个点到根节点的距离\(dis[i]\)作为\(i\ ...
- 4515: [Sdoi2016]游戏
4515: [Sdoi2016]游戏 链接 分析: 树链剖分 + 超哥线段树.注意细节. 代码: #include<cstdio> #include<algorithm> #i ...
- 【BZOJ4515】[Sdoi2016]游戏 树链剖分+线段树
[BZOJ4515][Sdoi2016]游戏 Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 1234567 ...
- Luogu 4069 [SDOI2016]游戏
BZOJ 4515 树链剖分 + 李超线段树 要求支持区间插入一条线段,然后查询一个区间内的最小值.可以使用李超线段树解决,因为要维护一个区间内的最小值,所以每一个结点再维护一个$res$表示这个区间 ...
- bzoj千题计划276:bzoj4515: [Sdoi2016]游戏
http://www.lydsy.com/JudgeOnline/problem.php?id=4515 把lca带进式子,得到新的式子 然后就是 维护树上一次函数取min 一个调了一下午的错误: 当 ...
- bzoj 4515: 游戏 树链剖分+线段树
题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=4515 题解: 先让我%一发lych大佬点我去看dalao的题解 讲的很详细. 这里纠正一 ...
- 【BZOJ 4515】【SDOI 2016 Round1 Day1 T3】游戏
考场上写了lct,可惜当时对标记永久化的理解并不是十分深刻,导致调一个错误的程序调了4h+,最后这道题爆0了QwQ 现在写了树链剖分,用标记永久化的线段树维护轻重链,对于$s\rightarrow l ...
- [BZOJ]1059 矩阵游戏(ZJOI2007)
虽然说是一道水题,但小C觉得还是挺有意思的,所以在这里mark一下. Description 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏.矩阵游戏在一个N*N黑白 ...
- bzoj 3875 骑士游戏 - spfa - 动态规划
Description [故事背景] 长期的宅男生活中,JYY又挖掘出了一款RPG游戏.在这个游戏中JYY会 扮演一个英勇的骑士,用他手中的长剑去杀死入侵村庄的怪兽. [问题描述] 在这个游戏中,J ...
随机推荐
- 关于apidoc文档生成不了的一个原因
前几天在写完API后,写注释文档,然后很习惯的去用apidoc取生成注释文档,但是奇怪的事发生了,没有注释的内容,也没有报错:注释代码如下: /* * @api {get} /applet/:id 根 ...
- 物联网蓝牙模块:DA14586蓝牙5模块很快到来
Dialog半导体的SmartBond系列的下一代产品---DA14586已经发布.该全新的系统级芯片(SoC)是公司首款支持最新蓝牙5.0规范的独立器件,为先进应用提供最低的功耗和无可比拟的功能. ...
- 新一代 CI 持续集成工具 flow.ci 正式开源
很高兴地宣布 flow.ci 在 Apache-2.0 协议下正式开源了.flow.ci 是国内首套开源持续集成(CI) 解决方案,帮助企业团队实现开发流程(build-test-deploy)自动化 ...
- 算法训练 最大的算式 DP
算法训练 最大的算式 时间限制:1.0s 内存限制:256.0MB 问题描述 题目很简单,给出N个数字,不改变它们的相对位置,在中间加入K个乘号和N-K-1个加号,(括号随便加)使最终结果 ...
- java变量和作用域以及成员变量的默认初始化
Java中的变量有成员变量和局部变量,定义在类中方法之外的变量成为成员变量或者成员字段(域),表示一个类所具有的属性,定义为类的成员变量的变量的作用于是整个类,该变量在定义的时候不需要初始化,在使用前 ...
- Java数据结构和算法(八)——递归
记得小时候经常讲的一个故事:从前有座山,山上有座庙,庙里有一个老和尚和一个小和尚,一天,老和尚给小和尚讲了一个故事,故事内容是“从前有座山,山上有座庙,庙里有一个老和尚和一个小和尚,一天,老和尚给小和 ...
- Netty之二进制文件传输
传输会话简要 客户端发起一个文本请求给服务器端, 服务器端解析里面文本, 返回文件给客户端, 客户端解析文件 服务器端 因为示例文件比较小, 所以没有做分段传输, 而是直接一次性把整个文件byte[] ...
- coursera普林斯顿算法课part1里Programming Assignment 2最后的extra challenge
先附上challenge要求: 博主最近在刷coursera普林斯顿大学算法课part1部分的作业,Programming Assignment2最后的这个extra challenge当初想了一段时 ...
- Java基础(十一) Stream I/O and Files
Java基础(十一) Stream I/O and Files 1. 流的概念 程序的主要任务是操纵数据.在Java中,把一组有序的数据序列称为流. 依据操作的方向,能够把流分为输入流和输出流两种.程 ...
- Android从无知到有知——NO.1
如期而至的软件设计大赛吹响了重生的号角.正如同我们的指导老师所说,这个暑假会影响你近几年的发展,也可能会决定你以后所走的道路. 是的.我身边就有非常好的样例,有些师哥师姐们常常跟我们说.软件大赛不仅使 ...