bzoj4515 [Sdoi2016]游戏 标记永久线段树+链剖+差分

树上路径问题还有一个比较好的差分就是利用到根节点的信息。。

题目的形式显然是一个一次函数,于是想到维护一次函数最小值

上升路径就是: A(d[x]-d[o])+B   =  -Ad[o]+(B-Ad[x]);

下降路径:       A(d[x]+d[o]-2*d[lca])+B = Ad[o]+(B+A(d[o]-2*d[lca]))

维护新函数  ,查询的时候分下去查。。由于需要区间答案,所以就需要up

标记永久就不需要down

注:

1、注意区分原树和线段树

2、注意查询的内容范围外、范围内

3、注意运算顺序

4、注意 long long 

5、注意d 和 dui 对应关系

码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 100005
#define ll long long
bool cz[N<<2];
int n,m,fu[N],sz[N],zhong[N<<1],tot,hou[N<<1],xia[N],wz[N],top[N],hson[N],cnt,op,lca,x,y,i,dui[N];
ll v[N<<1],d[N],a,b,c,kk[N<<2],bb[N<<2],A,B,ans,minn[N<<2];
void jian(int a,int b,ll c)
{
	++tot;hou[tot]=xia[a],xia[a]=tot,zhong[tot]=b,v[tot]=c;	
}
void jia(int a,int b,ll c)
{
	jian(a,b,c);
	jian(b,a,c);
}
void dfs1(int o,int fa,ll dis)
{
	sz[o]=1;
	d[o]=dis;
	fu[o]=fa;
	int i,nd;
	for(i=xia[o];i!=-1;i=hou[i])
	{
		nd=zhong[i];
		if(nd==fa)continue;
		dfs1(nd,o,dis+v[i]);
		if(sz[hson[o]]<sz[nd])hson[o]=nd;
		sz[o]+=sz[nd];
	}	
}
void dfs2(int o,int tap)
{
	top[o]=tap;
	int i,nd;
	wz[o]=++cnt;
	dui[cnt]=o;
	if(hson[o])dfs2(hson[o],tap);
	for(i=xia[o];i!=-1;i=hou[i])
	{
		nd=zhong[i];
		if(nd==fu[o]||nd==hson[o])continue;
		dfs2(nd,nd);	
	}
}
void up(int o,ll l,ll mid,ll r)
{
	minn[o]= cz[o]? min(kk[o]*d[dui[l]]+bb[o],kk[o]*d[dui[r]]+bb[o]):123456789123456789  ;
	minn[o]=min(min(minn[o<<1],minn[o<<1|1]),minn[o]);
}
void gai(int o,int  l,int  r)
{int mid=(l+r)/2;
	if(op==1)//上升 
	{
if(a<=l&&r<=b)
{
if(cz[o]==0||(kk[o]*d[dui[l]]+bb[o] >  -A*d[dui[l]]+(B+A*d[x])  && kk[o]*d[dui[r]]+bb[o] >  -A*d[dui[r]]+(B+A*d[x]) ))
{
	cz[o]=1;
	kk[o]=-A;
	bb[o]=B+A*d[x];
	minn[o]=min(minn[o],min( -A*d[dui[l]]+(B+A*d[x]),-A*d[dui[r]]+(B+A*d[x])));
	return ;
}
if(kk[o]*d[dui[l]]+bb[o] <=  -A*d[dui[l]]+(B+A*d[x])  && kk[o]*d[dui[r]]+bb[o] <=  -A*d[dui[r]]+(B+A*d[x]) )
{
	return ;
}
long double jd=1.0000000*((B+A*d[x])-bb[o])/(1.0000000*(kk[o]+A));
if(jd<=d[dui[mid]])
{
	gai(o<<1,l,mid);
	if(kk[o]*d[dui[r]]+bb[o] > - A*d[dui[r]]+(B+A*d[x]) )gai(o<<1|1,mid+1,r);
}else
{
	gai(o<<1|1,mid+1,r);
	if(kk[o]*d[dui[l]]+bb[o] >  -A*d[dui[l]]+(B+A*d[x]) )gai(o<<1,l,mid);	
}
}else
{
if(a<=mid)gai(o<<1,l,mid);
if(b>mid)gai(o<<1|1,mid+1,r);
}		
	}
	if(op==0)//下降
	{	
if(a<=l&&r<=b)
{
if(cz[o]==0||(kk[o]*d[dui[l]]+bb[o] >  A*d[dui[l]]+(B+A*(d[x]-2*d[lca]))  && kk[o]*d[dui[r]]+bb[o] >  A*d[dui[r]]+(B+A*(d[x]-2*d[lca])) ))
{
	cz[o]=1;
	kk[o]=A;
	bb[o]=B+A*(d[x]-2*d[lca]);
	minn[o]=min(min(A*d[dui[l]]+(B+A*(d[x]-2*d[lca])),  A*d[dui[r]]+(B+A*(d[x]-2*d[lca])) ),minn[o]);
	return ;
}
if(kk[o]*d[dui[l]]+bb[o] <=  A*d[dui[l]]+(B+A*(d[x]-2*d[lca]))  && kk[o]*d[dui[r]]+bb[o] <=  A*d[dui[r]]+(B+A*(d[x]-2*d[lca])) )
{
	return ;
}
long double jd=1.0000000*(B+A*(d[x]-2*d[lca])-bb[o])/(1.0000000*(kk[o]-A));
if(jd<=d[dui[mid]])
{
	gai(o<<1,l,mid);
	if(kk[o]*d[dui[r]]+bb[o] >   A*d[dui[r]]+(B+A*(d[x]-2*d[lca])) )gai(o<<1|1,mid+1,r);
}else
{
	gai(o<<1|1,mid+1,r);
	if(kk[o]*d[dui[l]]+bb[o] >  A*d[dui[l]]+(B+A*(d[x]-2*d[lca])) )gai(o<<1,l,mid);	
}
}else
{
if(a<=mid)gai(o<<1,l,mid);
if(b>mid)gai(o<<1|1,mid+1,r);
}		
	}
	if(op==2)
	{
if(cz[o])ans=min(min( d[dui[min(1ll*r,b)]]*kk[o]+bb[o], d[dui[max(1ll*l,a)]]*kk[o]+bb[o]    ),ans);
	if(a<=l&&r<=b)
	{		
ans=min(ans,minn[o]);
	return ;
}
		if(a<=mid)
		gai(o<<1,l,mid);		
		if(b>mid)
		gai(o<<1|1,mid+1,r);	
	}
if(l!=r)up(o,l,mid,r);	
}
int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
	if(d[top[x]]<d[top[y]])swap(x,y);
	x=fu[top[x]];
    }	
	if(d[x]>d[y])swap(x,y);
return x;
}
void work(int x,int y)
{  lca=LCA(x,y);
	while(top[x]!=top[lca])
	{
	a=wz[top[x]];b=wz[x];
	gai(1,1,n);
	x=fu[top[x]];
    }	
	a=wz[lca];b=wz[x];
	gai(1,1,n);
if(op==1)op=0;	
	while(top[y]!=top[lca])
	{
	a=wz[top[y]];b=wz[y];
	gai(1,1,n);
	y=fu[top[y]];
    }	
	a=wz[lca];b=wz[y];
	gai(1,1,n);
}
void csh(int o,int l,int r)
{
	minn[o]=123456789123456789;
	if(l==r)return ;
	int mid=(l+r)>>1;
	csh(o<<1,l,mid);
	csh(o<<1|1,mid+1,r);	
}
int main()
{
memset(xia,-1,sizeof(xia));
	scanf("%d%d",&n,&m);
	csh(1,1,n);
	for(i=1;i<n;i++)
	{
		scanf("%lld%lld%lld",&a,&b,&c);
		jia(a,b,c);
	}
	dfs1(1,1,0);
	dfs2(1,1);
	while(m--)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d%lld%lld",&x,&y,&A,&B);
			work(x,y);
		}else
		{
			ans=123456789123456789;
			scanf("%d%d",&x,&y);
			work(x,y);
			printf("%lld\n",ans);
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值