BZOJ.4515.[SDOI2016]游戏(树链剖分 李超线段树)
每次在路径上加的数是个一次函数,容易看出是树剖+李超线段树维护函数最小值。所以其实依旧是模板题。
横坐标自然是取个确定的距离标准。取每个点到根节点的距离\(dis[i]\)作为\(i\)的横坐标好了,这样对于同一条重链,横坐标还是递增的。
令\(w=LCA(u,v)\)。如果在\((u,v)\)路径上加入直线\(y=kx+b\):
对于在\(u\to w\)路径上的点,每个点\(i\)的横坐标就是\(dis_u-dis_i\),所以对于\(i\),\(y=k(dis_u-dis_i)+b=-k\cdot dis_i+k\cdot dis_u+b\),依旧是原坐标系一条\(k=-k,\quad b=k\cdot dis_u+b\)的直线。所以直接树剖+线段树维护即可。
另一条\(w\to v\)的路径同理。
线段树的每个节点维护它以及它儿子中的最小值\(mn[rt]\)(每个节点维护的都是一条直线,所以最小值显然就在两端点中取)。
区间查询的时候,如果当前区间完全包含于询问区间,就直接返回\(mn[rt]\);否则答案与当前点维护的线段两端点的函数值取个\(\min\),继续递归即可。
这样区间修改+树剖的复杂度是\(O(n\log^3n)\),你只要相信树剖+李超线段树的常数很小就好了。。
一个可能的解释:
一.因为一个线段的交点正好在区间的左数第二个点和右数第二个点的几率特别小,所以每次二分不一定要到末尾才结束,所以一般可以把交点平均在二分中间时刻停止,因此这里有个二分之一的常数;二.线段树不一定是最坏情况(每层都有两个点),所以这里有个约3/4的常数,又由于树链剖分不一定是最坏情况,所以这里又有个约1/2的常数,多亏了出题人良心),所以这题O(nlog^3n)是可以通过的。
注意线段树里的下标都是\(dis[ref[i]]\)。
写的时候的错误:
- Modify中的m和mx别混用。。
- 就算是第一次覆盖区间也要与mn[rt]取min。
- 各种细节。
[Update] 19.2.17
首先想到树剖+李超线段树。发现对于同一条重链上的点,以\(dis[i]\)作为自变量,它是单增的,所以就可以李超线段树维护了。
对于修改的形式,用\(dis[i]\)做自变量,拆一些常数出来即可。
查询时用经过的所有节点上的线段更新一次!(区间查询是用限制后的左右端点同时取\(\min\))维护了\(ans[rt]\)又怎样啊。。→_→
另外\(ans[rt]\)不是\(x=mid\)时的值,是区间左右端点函数值的最小值!在替换掉当前节点线段时\(ans[rt]\)也要与自己取\(\min\)(不一定当前线段左右端点的最小值都更小)!
第一次的代码:
//35240kb 9032ms
#include <cstdio>
#include <cctype>
#include <assert.h>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5;
const LL INF=123456789123456789ll;
int n,H[N],Enum,nxt[N<<1],to[N<<1],len[N<<1],fa[N],dep[N],sz[N],son[N],top[N],dfn[N],ref[N];
LL dis[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,ls
#define rson m+1,r,rs
#define S N<<2
// int have[S];
LL K[S],B[S],mn[S],Lx[S],Rx[S],Mx[S];
#undef S
#define Update(rt) mn[rt]=std::min(mn[rt],std::min(mn[ls],mn[rs]))
// void Init(int n)
// {
// for(int i=n<<2; i; --i) mn[i]=INF;
// }
void Build(int l,int r,int rt)
{
B[rt]=mn[rt]=INF, Lx[rt]=dis[ref[l]], Rx[rt]=dis[ref[r]];
if(l==r) return;
int m=l+r>>1; Mx[rt]=dis[ref[m]], Build(lson), Build(rson);
}
void Modify(int l,int r,int rt,int L,int R,LL k,LL b)
{
if(L<=l && r<=R)
{
LL lx=Lx[rt],rx=Rx[rt],l0=K[rt]*lx+B[rt],r0=K[rt]*rx+B[rt],l1=k*lx+b,r1=k*rx+b;
// if(!have[rt]) {have[rt]=1, K[rt]=k, B[rt]=b, mn[rt]=std::min(mn[rt],std::min(l1,r1)); return;}//当然要和当前节点取min啊!(由另一部分区间PushUp来的啊)
if(l0<=l1 && r0<=r1) return;
if(l1<=l0 && r1<=r0) {K[rt]=k, B[rt]=b, mn[rt]=std::min(mn[rt],std::min(l1,r1)); return;}//更新mn!
int m=l+r>>1;//m不是mx。。
LL mx=Mx[rt]; double p=1.0*(B[rt]-b)/(k-K[rt]);
if(l0<l1)
if(p<=(double)mx) Modify(lson,L,R,K[rt],B[rt]), K[rt]=k, B[rt]=b;
else Modify(rson,L,R,k,b);
else
if(p<=(double)mx) Modify(lson,L,R,k,b);
else Modify(rson,L,R,K[rt],B[rt]), K[rt]=k, B[rt]=b;
mn[rt]=std::min(mn[rt],std::min(l1,r1)), Update(rt);//!
return;
}
int m=l+r>>1;
if(L<=m) Modify(lson,L,R,k,b);
if(m<R) Modify(rson,L,R,k,b);
Update(rt);
}
LL Query(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return mn[rt];
LL res=INF;
if(B[rt]!=INF)
{
LL lx=std::max(l,L),rx=std::min(r,R);
res=std::min(K[rt]*dis[ref[lx]],K[rt]*dis[ref[rx]])+B[rt];
}
int m=l+r>>1;
if(L<=m) res=std::min(res,Query(lson,L,R));
if(m<R) res=std::min(res,Query(rson,L,R));
return res;
}
}T;
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now*f;
}
inline void AE(int w,int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;
}
inline int LCA(int u,int v)
{
while(top[u]!=top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
return dep[u]>dep[v]?v:u;
}
void DFS1(int x)
{
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x])
fa[v]=x, dep[v]=dep[x]+1, dis[v]=dis[x]+len[i], DFS1(v), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v],son[x]=v);
}
void DFS2(int x,int tp)
{
static int Index=0;
top[x]=tp, ref[dfn[x]=++Index]=x;
if(son[x])
{
DFS2(son[x],tp);
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x]&&v!=son[x]) DFS2(v,v);
}
}
void Modify(int u,int w,LL k,LL b)
{
while(top[u]!=top[w])
T.Modify(1,n,1,dfn[top[u]],dfn[u],k,b), u=fa[top[u]];
T.Modify(1,n,1,dfn[w],dfn[u],k,b);
}
LL Query(int u,int v)
{
LL res=INF;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
res=std::min(res,T.Query(1,n,1,dfn[top[u]],dfn[u])), u=fa[top[u]];
}
if(dep[u]>dep[v]) std::swap(u,v);
return std::min(res,T.Query(1,n,1,dfn[u],dfn[v]));
}
int main()
{
n=read(); int m=read();
for(int i=1; i<n; ++i) AE(read(),read(),read());
DFS1(1), DFS2(1,1), T.Build(1,n,1);
for(int i=1; i<=m; ++i)
switch(read())
{
case 1:
{
int u=read(),v=read(),w=LCA(u,v),k=read(),b=read();
Modify(u,w,-k,dis[u]*k+b), Modify(v,w,k,(dis[u]-(dis[w]<<1))*k+b);
break;
}
case 2: printf("%lld\n",Query(read(),read())); break;
}
return 0;
}
第二次写的代码(19.2.17):
换了种简便的写法,顺便还进了BZOJ第一页。很多细节还是忘了=-=
//28320kb 8608ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5;
const LL INF=123456789123456789ll;
int Enum,H[N],nxt[N<<1],to[N<<1],len[N<<1],dep[N],sz[N],fa[N],son[N],top[N],dfn[N],ref[N];
LL dis[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Line
{
LL k,b,val;
Line(LL k=0,LL b=INF,LL val=INF):k(k),b(b),val(val) {}
inline LL f(LL x)
{
return k*x+b;
}
};
struct Segment_Tree
{
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,ls
#define rson m+1,r,rs
#define S N<<2
LL dis[N],ans[S];
Line mn[S];
#undef S
void Init(const int n)
{
for(int i=1; i<=n; ++i) dis[i]=::dis[ref[i]];
for(int i=n<<2; i; --i) ans[i]=INF;
}
#define Update(rt) ans[rt]=std::min(ans[rt],std::min(ans[ls],ans[rs]))
void Modify(int l,int r,int rt,Line v)
{
int m=l+r>>1;
v.val=v.f(dis[m]);
if(v.val<mn[rt].val) ans[rt]=std::min(ans[rt],std::min(v.f(dis[l]),v.f(dis[r]))), std::swap(v,mn[rt]);
if(l==r) return;
if(v.f(dis[l])>=mn[rt].f(dis[l]) && v.f(dis[r])>=mn[rt].f(dis[r])) return;
if(v.k>mn[rt].k) Modify(lson,v);
else Modify(rson,v);
Update(rt);
}
void ModifyS(int l,int r,int rt,int L,int R,Line v)
{
if(L<=l && r<=R) {Modify(l,r,rt,v); return;}
int m=l+r>>1;
if(L<=m) ModifyS(lson,L,R,v);
if(m<R) ModifyS(rson,L,R,v);
Update(rt);
}
LL Query(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return ans[rt];
LL res=std::min(mn[rt].f(dis[std::max(l,L)]),mn[rt].f(dis[std::min(r,R)]));//!!!
int m=l+r>>1;
if(L<=m) res=std::min(res,Query(lson,L,R));
if(m<R) res=std::min(res,Query(rson,L,R));
return res;
}
}T;
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now*f;
}
inline void AE(int w,int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;
}
inline int LCA(int u,int v)
{
while(top[u]!=top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
return dep[u]>dep[v]?v:u;
}
void DFS1(int x)
{
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x])
dep[v]=dep[x]+1, dis[v]=dis[x]+len[i], fa[v]=x, DFS1(v), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v],son[x]=v);
}
void DFS2(int x,int tp)
{
static int Index=0;
ref[dfn[x]=++Index]=x, top[x]=tp;
if(son[x])
{
DFS2(son[x],tp);
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x] && v!=son[x]) DFS2(v,v);
}
}
#define S 1,n,1
void Modify(const int n,int u,int w,Line v)
{
while(top[u]!=top[w]) T.ModifyS(S,dfn[top[u]],dfn[u],v), u=fa[top[u]];
T.ModifyS(S,dfn[w],dfn[u],v);
}
void Query(const int n)
{
int u=read(),v=read(); LL res=INF;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
res=std::min(res,T.Query(S,dfn[top[u]],dfn[u])), u=fa[top[u]];
}
if(dep[u]>dep[v]) std::swap(u,v);
res=std::min(res,T.Query(S,dfn[u],dfn[v]));
printf("%lld\n",res);
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
const int n=read(),m=read();
for(int i=1; i<n; ++i) AE(read(),read(),read());
DFS1(1), DFS2(1,1), T.Init(n);
for(int i=1; i<=m; ++i)
switch(read())
{
case 1:
{
int u=read(),v=read(),w=LCA(u,v); LL a=read(),b=read();
Modify(n,u,w,Line(-a,b+dis[u]*a,0)), Modify(n,v,w,Line(a,b+a*(dis[u]-(dis[w]<<1)),0)); break;
}
case 2: Query(n); break;
}
return 0;
}
BZOJ.4515.[SDOI2016]游戏(树链剖分 李超线段树)的更多相关文章
- [bzoj4515][Sdoi2016]游戏-树链剖分+李超线段树
Brief Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,A ...
- bzoj 4034 [HAOI2015] T2(树链剖分,线段树)
4034: [HAOI2015]T2 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1536 Solved: 508[Submit][Status] ...
- bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 10677 Solved: 4313[Submit ...
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1272 Solved: 451[Submit][Status ...
- bzoj 2243 [SDOI2011]染色(树链剖分,线段树)
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4637 Solved: 1726[Submit][Status ...
- 【BZOJ5507】[GXOI/GZOI2019]旧词(树链剖分,线段树)
[BZOJ5507][GXOI/GZOI2019]旧词(树链剖分,线段树) 题面 BZOJ 洛谷 题解 如果\(k=1\)就是链并裸题了... 其实\(k>1\)发现还是可以用类似链并的思想,这 ...
- poj 3237 Tree(树链剖分,线段树)
Tree Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 7268 Accepted: 1969 Description ...
- HDU 4366 Successor(树链剖分+zkw线段树+扫描线)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=4366 [题目大意] 有一个公司,每个员工都有一个上司,所有的人呈树状关系,现在给出每个人的忠诚值和 ...
- 【BZOJ3531】旅行(树链剖分,线段树)
[BZOJ3531]旅行(树链剖分,线段树) 题面 Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教 ...
随机推荐
- vue-cli3.0 使用postcss-plugin-px2rem(推荐)和 postcss-pxtorem(postcss-px2rem)自动转换px为rem 的配置方法;
如何在vue-cli3.0中使用postcss-plugin-px2rem 插件 插件的作用是 自动将vue项目中的px转换为rem . 为什么这三个中要推荐 postcss-plugin-px2r ...
- Allegro PCB Design GXL (legacy) 由零散的对象构成一个Shape
Allegro PCB Design GXL (legacy) version 16.6-2015 从DXF文件中导入板框之后,发现板框是由Line Segment.Arc Segment等对象组成, ...
- python Com接口测试
import comtypes.client as cc import comtypes tlb_id = comtypes.GUID("{D85C6069-D628-4276-93C3-9 ...
- MyEclipes相关配置
0. MyEclipes10 相关下载资源(私人珍藏版) 链接:http://pan.baidu.com/s/1eSIdObS密码:0cjy 1. myEclipes连接Tomcat http://w ...
- java运行环境增加字体
背景 今天在使用jfreeChart时,显示中文乱码,如下图: 环境:Ubuntu 13.04 64bit java7 tomcat 7.0.42 解决方法--增加系统字体 0. 安装环境包 su ...
- P0505
算法训练 P0505 时间限制:1.0s 内存限制:256.0MB 一个整数n的阶乘可以写成n!,它表示从1到n这n个整数的乘积.阶乘的增长速度非常快,例如,13!就已经比较大了 ...
- Java+selenium之WebDriver的cookie,等待等高级操作(五)
1. 操作cookie // 增加一个 name = "name",value="value" 的 cookie Cookie cookie = new Coo ...
- asp.net core WebApi 返回 HttpResponseMessage
ASP.NET WebApi 2 中的示例代码: [Route("values/{id}")] public async Task<HttpResponseMessage&g ...
- 一脸懵逼学习oracle(图形化界面操作---》PLSQL图形化界面)
1:经过几天的折腾,终于将oracle安装成功,创建用户,授权等等操作,接下来就安安心心学习oracle: 安装好PLSQL图形化界面和汉化以后(过程自己百度吧,百度more and more),登录 ...
- [转] React之Immutable学习记录
从问题说起:熟悉 React 组件生命周期的话都知道:调用 setState 方法总是会触发 render 方法从而进行 vdom re-render 相关逻辑,哪怕实际上你没有更改到 Compone ...