1. 程式人生 > >【CF932F】Escape Through Leaf 啟發式合並set維護凸包

【CF932F】Escape Through Leaf 啟發式合並set維護凸包

線段 font d+ strong 子節點 線段樹合並 break clas 葉子

【CF932F】Escape Through Leaf

題意:給你一棵n個點的樹,每個點有樹形ai和bi,如果x是y的祖先,則你可以從x花費$a_x\times b_y$的費用走到y(費用可以為負)。對於每個點,求從這個點開始走到某個葉子節點的最小費用。

$n\le 10^5,|a_i|,|b_i|\le 10^5$

題解:用f[x]表示x的答案,顯然f[x]=min{f[y]+a[x]*b[y]}是一個凸包,我們可以用set維護凸包,到時候自底向上做一次啟發式合並就行了(也可以線段樹合並)。

用叉積會爆long long差評~

#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
const int maxn=100010;
typedef long long ll;
struct node
{
	ll x,y;
	node () {}
	node (ll a,ll b) {x=a,y=b;}
	bool operator < (const node &a) const {return (x==a.x)?(y<a.y):(x<a.x);}
	long double operator * (const node &a) const {return (long double)x*a.y-(long double)y*a.x;}
	node operator - (const node &a) const {return node(x-a.x,y-a.y);}
};
set<node> s[maxn];
set<node>::iterator it,iit;
int n,cnt,tot;
int to[maxn<<1],nxt[maxn<<1],head[maxn],siz[maxn],rt[maxn];
ll va[maxn],vb[maxn],f[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
inline void add(int a,int b)
{
	to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++;
}
inline void insert(int a,node x)
{
	it=s[a].lower_bound(x);
	if(it!=s[a].end()&&(*it).x==x.x)	s[a].erase(it),it=s[a].lower_bound(x);
	if(it!=s[a].begin())
	{
		it--;
		if((*it).x==x.x)	return ;
		it++;
	}
	node l,r;
	if(it!=s[a].end()&&it!=s[a].begin())
	{
		r=*it,it--,l=*it;
		if((x-l)*(r-x)<=0)	return ;
	}
	while(1)
	{
		it=s[a].lower_bound(x);
		if(it==s[a].end())	break;
		l=*it,it++;
		if(it==s[a].end())	break;
		r=*it;
		if((l-x)*(r-l)<=0)	s[a].erase(l);
		else	break;
	}
	while(1)
	{
		it=s[a].lower_bound(x);
		if(it==s[a].begin())	break;
		it--,r=*it;
		if(it==s[a].begin())	break;
		it--,l=*it;
		if((r-l)*(x-r)<=0)	s[a].erase(r);
		else	break;
	}
	s[a].insert(x);
}
inline int merge(int a,int b)
{
	if(s[a].size()<s[b].size())	swap(a,b);
	for(iit=s[b].begin();iit!=s[b].end();iit++)	insert(a,*iit);
	s[b].clear();
	return a;
}
void dfs(int x,int fa)
{
	rt[x]=++tot;
	for(int i=head[x],y;i!=-1;i=nxt[i])	if(to[i]!=fa)
	{
		y=to[i],dfs(y,x),rt[x]=merge(rt[x],rt[y]);
	}
	if(!s[rt[x]].size())	f[x]=0;
	else
	{
		int l=-100001,r=100001,mid;
		node a,b;
		while(l<r)
		{
			mid=(l+r)>>1;
			it=s[rt[x]].lower_bound(node(mid,-1ll<<60));
			if(it==s[rt[x]].begin())
			{
				l=mid+1;
				continue;
			}
			if(it==s[rt[x]].end())
			{
				r=mid;
				continue;
			}
			b=*it,it--,a=*it;
			if((b.y-a.y)<=-va[x]*(b.x-a.x))	l=mid+1;
			else	r=mid;
		}
		it=s[rt[x]].lower_bound(node(l-1,-1ll<<60));
		f[x]=va[x]*(*it).x+(*it).y;
	}
	insert(rt[x],node(vb[x],f[x]));
}
int main()
{
	//freopen("cf932F.in","r",stdin);
	//freopen("cf932F.out","w",stdout);
	n=rd();
	int i,a,b;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++)	va[i]=rd();
	for(i=1;i<=n;i++)	vb[i]=rd();
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
	dfs(1,0);
	for(i=1;i<=n;i++)	printf("%lld ",f[i]);
	return 0;
}//3 1 -1 1 -1 -1 -1 1 2 2 3

【CF932F】Escape Through Leaf 啟發式合並set維護凸包