zoukankan      html  css  js  c++  java
  • BZOJ.4515.[SDOI2016]游戏(树链剖分 李超线段树)

    BZOJ
    洛谷


    每次在路径上加的数是个一次函数,容易看出是树剖+李超线段树维护函数最小值。所以其实依旧是模板题。
    横坐标自然是取个确定的距离标准。取每个点到根节点的距离(dis[i])作为(i)的横坐标好了,这样对于同一条重链,横坐标还是递增的。

    (w=LCA(u,v))。如果在((u,v))路径上加入直线(y=kx+b)
    对于在(u o w)路径上的点,每个点(i)的横坐标就是(dis_u-dis_i),所以对于(i)(y=k(dis_u-dis_i)+b=-kcdot dis_i+kcdot dis_u+b),依旧是原坐标系一条(k=-k,quad b=kcdot dis_u+b)的直线。所以直接树剖+线段树维护即可。
    另一条(w o v)的路径同理。

    线段树的每个节点维护它以及它儿子中的最小值(mn[rt])(每个节点维护的都是一条直线,所以最小值显然就在两端点中取)。
    区间查询的时候,如果当前区间完全包含于询问区间,就直接返回(mn[rt]);否则答案与当前点维护的线段两端点的函数值取个(min),继续递归即可。

    这样区间修改+树剖的复杂度是(O(nlog^3n)),你只要相信树剖+李超线段树的常数很小就好了。。
    一个可能的解释

    一.因为一个线段的交点正好在区间的左数第二个点和右数第二个点的几率特别小,所以每次二分不一定要到末尾才结束,所以一般可以把交点平均在二分中间时刻停止,因此这里有个二分之一的常数;二.线段树不一定是最坏情况(每层都有两个点),所以这里有个约3/4的常数,又由于树链剖分不一定是最坏情况,所以这里又有个约1/2的常数,多亏了出题人良心),所以这题O(nlog^3n)是可以通过的。

    注意线段树里的下标都是(dis[ref[i]])

    写的时候的错误:

    1. Modify中的m和mx别混用。。
    2. 就算是第一次覆盖区间也要与mn[rt]取min。
    3. 各种细节。

    [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
    ",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
    ",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;
    }
    
  • 相关阅读:
    《分析服务从入门到精通读书笔记》第四章、维度部署浏览篇(3)
    集群(cluster)原理(转)
    Atitit 前端测试最简化内嵌web服务器 php 与node.js 目录 1.1. php内置Web Server 1 1.2. Node的 2 Node的比较麻烦些。。Php更加简单
    Atitit mybatis使用简明教程 目录 1.1. Mybatis.xml 配置文件 1 1.2. Datamp.xml mapper文件主要作用是分模块放sql语句 2 1.3. 查询
    Atitit 业务流程执行引擎的实现 目录 1.1. 引擎实现语言java js php等 1 1.2. 流程语言 xml sql js等 业务流程定义语言规范总结 1 1.3. 实体方法定义 2
    Atitit 常见软件设计图纸总结 目录 1.1. ui原型图与html 2 1.2. 业务逻辑 伪代码 各种uml图 2 1.3. 总体设计图纸 结构图 层次图 架构图 2 1.4. 业务逻辑
    Atitit velocity 模板引擎使用法 目录 1.1. 1.4 Context 1 1.1.1. 1.4.1 Context 基本概念 1 1.2. .3不同模式下使用velocity 1
    Atitit mysql存储过程编写指南 1. 定义变量 1 1.1. 变量名以@开头用户变量 会话变量 1 1.2. 以declare关键字声明 存储过程变量 2 1.3. @是用户自定义变量,
    Atitit 算法的艺术 attilax艾提拉著v7 t88.docx Atitit 算法之道 attilax著 1. 文本方面的编码算法 3 1.1. Base64 htmlencode url
    Atitt 常见的拖欠费用的公司与个人黑名单 陕西西部安全网 20072008 西安 一个月 25000 西安盛世涅槃 2007 西安 1m 25000 Sicyi msc 2007 n
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10081074.html
Copyright © 2011-2022 走看看