bzoj4515 游戏 树链剖分

       首先可以把修改转换一下,因为那个dis非常不爽。显然s~t的路径有s~lca和lca~t组成。令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s]),lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x]+(b+a*(d[s]-d[x]*2)),这样就可以得到新的a'和b',然后一个点的值就相当于a'*d[x]+b'了,这样就只和a',b'和d[x]有关了。

       注意到在树链剖分对应的线段树区间中,相连的一部分它们在树种也是相连的,这样就满足d[]单调递增,观察a'*d[x]+b',发现这相当于直线f(x)=a'x+b在d[x](两个x不是一个意思)处的取值!那么就相当于用线段树维护一个区间中的若干条直线在每个部分的最小值。

       不妨考虑标记永久化(这里只是一定程度上的永久化但还是要下传的)。让线段树中的一个节点只对应一条直线,那么如果在这个区间加入一条直线怎么办呢?要分类讨论,设新加入的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同理。

       实际上这就是线段树维护半平面交的过程~~~~~

       询问就简单多了,直接用标记永久化的线段树的方法更新即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 100005
#define inf 123456789123456789LL
using namespace std;

int n,m,tot,dfsclk,fst[N],pnt[N<<1],len[N<<1],nxt[N<<1],fa[N],son[N],sz[N],anc[N],pos[N],id[N];
int lk[N<<2]; ll lb[N<<2],val[N<<2],d[N],ans; bool tag[N<<2];
int read(){
	int x=0,fu=1; char ch=getchar();
	while (ch<'0' || ch>'9'){ if (ch=='-') fu=-1; ch=getchar(); }
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x*fu;
}
void add(int x,int y,int z){
	pnt[++tot]=y; len[tot]=z; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x){
	int p; sz[x]=1;
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (fa[x]!=y){
			fa[y]=x; d[y]=d[x]+len[p];
			dfs(y); sz[x]+=sz[y];
			if (sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
void divide(int x,int tp){
	pos[x]=++dfsclk; id[dfsclk]=x; anc[x]=tp;
	if (son[x]) divide(son[x],tp); int p;
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[x] && y!=son[x]) divide(y,y);
	}
}
int lca(int x,int y){
	for (; anc[x]!=anc[y]; x=fa[anc[x]])
		if (d[anc[x]]<d[anc[y]]) swap(x,y);
	return (d[x]<d[y])?x:y;
}
void maintain(int k,int l,int r){
	if (l<r) val[k]=min(val[k<<1],val[k<<1|1]); else val[k]=inf;
	if (tag[k]) val[k]=min(val[k],min(d[id[l]]*lk[k],d[id[r]]*lk[k])+lb[k]);
}
void build(int k,int l,int r){
	val[k]=inf;
	if (l<r){
		int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r);
	}
}
void up(int k,int l,int r,int u,ll v){
	if (!tag[k]){ tag[k]=1; lk[k]=u; lb[k]=v; } else{
		ll x1=d[id[l]]*u+v,y1=d[id[r]]*u+v,x2=d[id[l]]*lk[k]+lb[k],y2=d[id[r]]*lk[k]+lb[k];
		int mid=(l+r)>>1;
		if (x1<=x2 && y1<=y2){
			lk[k]=u; lb[k]=v;
		} else if (x1>=x2 && y1>=y2)	 return;
		else if (u<lk[k]){
			ll tmp=(v-lb[k])/(lk[k]-u)+1;
			if (tmp<=d[id[mid]]){
				swap(u,lk[k]); swap(v,lb[k]);
				up(k<<1,l,mid,u,v);
			} else up(k<<1|1,mid+1,r,u,v);
		} else{
			ll tmp=(lb[k]-v-1)/(u-lk[k]);
			if (tmp>d[id[mid]]){
				swap(u,lk[k]); swap(v,lb[k]);
				up(k<<1|1,mid+1,r,u,v);
			} else up(k<<1,l,mid,u,v);
		}
	}
	maintain(k,l,r);
}
void ins(int k,int l,int r,int x,int y,int u,ll v){
	if (l==x && r==y){ up(k,l,r,u,v); return; }
	int mid=(l+r)>>1;
	if (y<=mid) ins(k<<1,l,mid,x,y,u,v); else
	if (x>mid) ins(k<<1|1,mid+1,r,x,y,u,v); else{
		ins(k<<1,l,mid,x,mid,u,v); ins(k<<1|1,mid+1,r,mid+1,y,u,v);
	}
	maintain(k,l,r);
}
void qry(int k,int l,int r,int x,int y){
	if (l==x && r==y){ ans=min(ans,val[k]); return; }
	if (tag[k]) ans=min(ans,min(d[id[x]]*lk[k],d[id[y]]*lk[k])+lb[k]);
	int mid=(l+r)>>1;
	if (y<=mid) qry(k<<1,l,mid,x,y); else
	if (x>mid) qry(k<<1|1,mid+1,r,x,y); else{
		qry(k<<1,l,mid,x,mid); qry(k<<1|1,mid+1,r,mid+1,y);
	}
}
int main(){
	n=read(); m=read(); int i,x,y,z;
	for (i=1; i<n; i++){
		x=read(); y=read(); z=read();
		add(x,y,z); add(y,x,z);
	}
	dfs(1); divide(1,1); build(1,1,n); int k,u,v; ll tmp;
	while (m--){
		k=read();
		if (k==1){
			x=read(); y=read(); u=read(); v=read(); z=lca(x,y);
			tmp=d[x]*u+v;
			for (; anc[x]!=anc[z]; x=fa[anc[x]])
				ins(1,1,n,pos[anc[x]],pos[x],-u,tmp);
			ins(1,1,n,pos[z],pos[x],-u,tmp);
			tmp-=(d[z]<<1)*u;
			for (; anc[y]!=anc[z]; y=fa[anc[y]])
				ins(1,1,n,pos[anc[y]],pos[y],u,tmp);
			ins(1,1,n,pos[z],pos[y],u,tmp);
		} else{
			x=read(); y=read(); ans=inf;
			for (; anc[x]!=anc[y]; x=fa[anc[x]]){
				if (d[anc[x]]<d[anc[y]]) swap(x,y);
				qry(1,1,n,pos[anc[x]],pos[x]);
			}
			if (d[x]>d[y]) swap(x,y); qry(1,1,n,pos[x],pos[y]);
			printf("%lld\n",ans);
		}
	}
	return 0;
}


by lych

2016.4.15

你可能感兴趣的:(线段树,DFS,半平面交,树链剖分,标记永久化)