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条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教 ...
随机推荐
- kali linux宿主机和虚拟机互访实现方案
1.攻防模拟中,将DVWA安装到自己的宿主机中,在kali Linux中通过sqlmap和其他工具启动嗅探攻击,需要配置网络.虚拟机采用桥接方式,并复制Mac地址状况. 2.查看各自系统下的IP地址. ...
- spfa+01 规划
尼玛的哪里错了.. /* 在有向图上找一个环,使结点权值和/边权和的比例值最大 01规划,设比例为l,那么将每条边的权值改成a[u]-l*w,如果有正权环,则比例l可行 如何判图中存在正权环?将 权值 ...
- lightoj1214 大数取模模板
#include<bits/stdc++.h> using namespace std; #define maxn 300 #define ll long long ll a,b; ]; ...
- tinymce-vue富文本编辑器(翻译)
官方Tinymce Vue组件 翻译来自:https://github.com/tinymce/tinymce-vue 官方文档:https://www.tiny.cloud/docs/general ...
- Vue-cli 创建的项目配置跨域请求(通过反向代理)---配置多个代理--axios请求
问题描述: 使用 Vue-cli 创建的项目,开发地址是 localhost:8080,需要访问 localhost:9000 或https://m.maoyan.com或http://image.b ...
- SSM 三大框架---事务处理
SSM 三大框架---事务处理 原创 2016年05月12日 20:57:03 标签: spring / J2EE / java / 框架 / 事务 7010 在学习三大框架的时候,老师说事务处理是最 ...
- 饮冰三年-人工智能-linux-05 Linux进程
1:top 命令,查看cpu使用情况.(由于top是实时刷新,占用内存比较大) P:按照cpu使用率降序排列 M:按照内存使用率降序排列 2:free 命令,查看内存使用情况 free -m 以M为单 ...
- 从0开始简单使用git进行项目开发【SourceTree+Coding.net】
一.什么是git? 含义:Git 是 Linux 发明者 Linus 开发的一款新时代的版本控制系统,相比于原来的svn系统更加简单和实用 作用: 熟悉编程的知道,我们在软件开发中源代码其实是最重要的 ...
- mysql的下载与安装
官网 下载 https://www.mysql.com/ 依次点击:downloads>community>mysql community server,如图所示 image.png ...
- 【AtCoder】AGC031
A - Colorful Subsequence 答案是 \(\prod_{c = 'a'}^{'z'} (cnt[c] + 1)\) #include <bits/stdc++.h> # ...