luogu P4069 [SDOI2016]游戏

强行把李超线段树搬到树上。然后这题我调了两天,最后发现数组开小了。。。

我们发现题目就是把凸包放到树上然后求最值。用树剖把树化为序列,然后直接李超线段树就可以了。

惨痛的教训:一共插入的线段数量是 \(O(nlogn)\) 级别的,数组一定要开大/lb。

代码:

#pragma GCC optimize("Ofast,unroll-loops")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define int long long

using namespace std;

const int N=1000009,INF=123456789123456789ll;
int n,head[N],cnt,m,Index,Min_[N];
struct Edge
{
	int nxt,to,w;
}g[N*2];
struct Dot
{
	int dfn,rev,siz,son,dep,top,fa,dis;
	#define dfn(x) a[x].dfn
	#define rev(x) a[x].rev
	#define s(x) a[x].siz
	#define son(x) a[x].son
	#define t(x) a[x].top
	#define f(x) a[x].fa
	#define dep(x) a[x].dep
	#define dis(x) a[x].dis
}a[N];
struct LC_Tree
{
	int K[N],B[N],Min[N*4],cnt,tag[N*4];
	
	void Insert(int a,int b) { K[++cnt]=a,B[cnt]=b; }
	
	int val(int id,int k) { return K[id]*dis(rev(k))+B[id]; }
	
	void Modify(int k,int l,int r,int id)
	{
		if(l==r)
		{
			tag[k]=val(id,l)<val(tag[k],l)?id:tag[k];
			Min[k]=val(tag[k],l);
			return;
		}
		int mid=l+r>>1;
		if(K[id]>K[tag[k]])
		{
			if(val(id,mid)<val(tag[k],mid))
				Modify(k<<1|1,mid+1,r,tag[k]),tag[k]=id;
			else
				Modify(k<<1,l,mid,id);
		}
		else
		{
			if(val(id,mid)<val(tag[k],mid))
				Modify(k<<1,l,mid,tag[k]),tag[k]=id;
			else
				Modify(k<<1|1,mid+1,r,id);
		}
		Min[k]=min(min(val(tag[k],l),val(tag[k],r)),min(Min[k<<1],Min[k<<1|1]));
	}
	
	void Change(int k,int l,int r,int x,int y,int id)
	{
		if(l>=x&&r<=y)
		{
//			printf("Modify:%lld %lld %lld %lld\n",k,l,r,id);
			Modify(k,l,r,id);
			return;
		}
		int mid=l+r>>1;
		if(mid>=x)
			Change(k<<1,l,mid,x,y,id);
		if(mid<y)
			Change(k<<1|1,mid+1,r,x,y,id);
		Min[k]=min(min(val(tag[k],l),val(tag[k],r)),min(Min[k<<1],Min[k<<1|1]));
	}
	
	int Query(int k,int l,int r,int x,int y)
	{
		if(l>=x&&r<=y) return Min[k];
		int mid=l+r>>1,res=INF;
		res=min(val(tag[k],max(l,x)),val(tag[k],min(r,y)));
		if(mid>=x)
			res=min(res,Query(k<<1,l,mid,x,y));
		if(mid<y)
			res=min(res,Query(k<<1|1,mid+1,r,x,y));
		return res;
	}
	
	void build(int k,int l,int r)
	{
		Min[k]=INF;
		if(l==r)
			return;
		int mid=l+r>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
	}
}T;

void add(int from,int to,int w)
{
	g[++cnt].nxt=head[from];
	g[cnt].to=to;
	g[cnt].w=w;
	head[from]=cnt;
}

void dfs(int x,int fa)
{
	s(x)=1;
	for (int i=head[x];i;i=g[i].nxt)
	{
		int v=g[i].to;
		if(v==fa)
			continue;
		f(v)=x,dep(v)=dep(x)+1,dis(v)=dis(x)+g[i].w;
		dfs(v,x);
		s(x)+=s(v);
		if(s(son(x))<s(v))
			son(x)=v;
	}
//	printf("%lld %lld\n",x,son(x));
}

void DFS(int x,int top)
{
	dfn(x)=++Index,rev(Index)=x,t(x)=top;
	if(son(x))
		DFS(son(x),top);
	for (int i=head[x];i;i=g[i].nxt)
	{
		int v=g[i].to;
		if(v==f(x)||v==son(x))
			continue;
		DFS(v,v);
	}
//	printf("%lld %lld\n",x,t(x));
}

void init()
{
	scanf("%lld %lld",&n,&m);
	for (int i=1,x,y,z;i<n;i++)
	{
		scanf("%lld %lld %lld",&x,&y,&z);
		add(x,y,z),add(y,x,z);
	}
	dfs(1,-1);
	DFS(1,1);
}

int lca(int x,int y)
{
	while(t(x)!=t(y))
	{
		if(dep(t(x))<dep(t(y)))
			swap(x,y);
		x=f(t(x));
	}
	if(dep(x)>dep(y))
		swap(x,y);
	return x;
}

int Dis(int x,int y) { return dis(x)+dis(y)-2*dis(lca(x,y)); }

void Modify_Path(int x,int y,int A,int B)
{
	int LCA=lca(x,y),S=x;
 	while(t(x)!=t(LCA))
	{
//		for (int i=dfn(t(x));i<=dfn(x);i++)
//			Min_[i]=min(Min_[i],-A*dis(rev(i))+A*Dis(t(x),S)+B+A*dis(t(x)));
		T.Insert(-A,A*Dis(t(x),S)+B+A*dis(t(x)));
		T.Change(1,1,n,dfn(t(x)),dfn(x),T.cnt);
		x=f(t(x));
	}
//	for (int i=dfn(LCA);i<=dfn(x);i++)
//		Min_[i]=min(Min_[i],-A*dis(rev(i))+A*Dis(LCA,S)+B+A*dis(LCA));
	T.Insert(-A,A*Dis(LCA,S)+B+A*dis(LCA));
//	printf("%lld %lld\n",dfn(LCA),dfn(x));
	T.Change(1,1,n,dfn(LCA),dfn(x),T.cnt);
	while(t(y)!=t(LCA))
	{
//		for (int i=dfn(t(y));i<=dfn(y);i++)
//			Min_[i]=min(Min_[i],A*dis(rev(i))+A*Dis(t(y),S)+B-A*dis(t(y)));
		T.Insert(A,A*Dis(t(y),S)+B-A*dis(t(y)));
		T.Change(1,1,n,dfn(t(y)),dfn(y),T.cnt);
		y=f(t(y));
	}
//	for (int i=dfn(LCA);i<=dfn(y);i++)
//		Min_[i]=min(Min_[i],A*dis(rev(i))+A*Dis(LCA,S)+B-A*dis(LCA));
	T.Insert(A,A*Dis(LCA,S)+B-A*dis(LCA));
	T.Change(1,1,n,dfn(LCA),dfn(y),T.cnt);
}

int Query_Path(int x,int y)
{
	int ans=INF;
	while(t(x)!=t(y))
	{
		if(dep(t(x))<dep(t(y)))
			swap(x,y);
//		for (int i=dfn(t(x));i<=dfn(x);i++)
//			ans=min(ans,Min_[i]);
		ans=min(ans,T.Query(1,1,n,dfn(t(x)),dfn(x)));
		x=f(t(x));
	}
	if(dep(x)>dep(y))
		swap(x,y);
//	for (int i=dfn(x);i<=dfn(y);i++)
//		ans=min(ans,Min_[i]);
	ans=min(ans,T.Query(1,1,n,dfn(x),dfn(y)));
	return ans;
}

void work()
{
	T.B[0]=123456789123456789ll;
	T.build(1,1,n);
	for (int i=1;i<=n;i++)
		Min_[i]=INF;
//	puts("chishi");
	while(m--)
	{
		int opt,A,B,C,D;
		scanf("%lld",&opt);
		if(opt==1)
			scanf("%lld %lld %lld %lld",&A,&B,&C,&D),Modify_Path(A,B,C,D);
		else
			scanf("%lld %lld",&A,&B),printf("%lld\n",Query_Path(A,B));
	}
}

signed main()
{
	init();
	work();
	return 0;
}
posted @ 2020-07-08 01:13  With_penguin  阅读(208)  评论(0编辑  收藏  举报