CodeForces 932F Escape Through Leaf [set启发式合并+维护凸包+二分]

题意:给你一颗树,每一个节点有两个属性,ai与bi,每一个点只能跳到以它为根的子树上的点,若从x跳到y,则跳的费用是ax*by,求每一个点跳到叶子节点最小费用和。

题解:我们先考虑每一个节点x,ans[x]=min(ans[y]+a[x]*b[y]),我们考虑所有子节点的答案优先情况,若节点j优于节点i,则有:

我们可以将子节点按照x递增的方式排序(b[j]>b[i])。

整理可得:


根据这个式子,假设有a,b,c三个点。

CodeForces 932F Escape Through Leaf [set启发式合并+维护凸包+二分]_第1张图片

若kab<=-a[x]则a比b优,又因为c比a优,所以a这个点肯定不需要考虑进去,因此,我们可以维护一个下凸包,在下凸包中二分最优点即可得到当前点的最优值。

然后对于每一个点,我们用set维护这个坐标系,通过启发式合并传递结果。

AC代码:

#include
#include
#include
#include
#define inf 1000000000000000000ll
#define N 100005
using namespace std;
typedef long long ll;
ll a[N],b[N];
vectorvt[N];
ll ans[N],who[N];

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)?(yst[N];
set::iterator it,itt;
void insert(ll a,node x)
{
    it=st[a].lower_bound(x);
    if(it!=st[a].end()&&(*it).x==x.x)    
		st[a].erase(it),it=st[a].lower_bound(x);
    if(it!=st[a].begin())
    {
        it--;
        if((*it).x==x.x)    return ;
        it++;
    }
    node l,r;
    if(it!=st[a].end()&&it!=st[a].begin())
    {
        r=*it,it--,l=*it;
        if((x-l)*(r-x)<=0)   return ;
    }
    while(1)
    {
        it=st[a].lower_bound(x);
        if(it==st[a].end())  break;
        l=*it,it++;
        if(it==st[a].end())  break;
        r=*it;
        if((l-x)*(r-l)<=0)   st[a].erase(l);
        else    break;
    }
    while(1)
    {
        it=st[a].lower_bound(x);
        if(it==st[a].begin())    break;
        it--,r=*it;
        if(it==st[a].begin())    break;
        it--,l=*it;
        if((r-l)*(x-r)<=0)   st[a].erase(r);
        else    break;
    }
    st[a].insert(x);
}
ll merge(ll u,ll v)
{
	if(st[u].size()>1;
			it=st[who[u]].lower_bound(node(mid,-inf));
			if(it==st[who[u]].begin())
			{
				l=mid+1;
				continue;
			}
			if(it==st[who[u]].end())
			{
				r=mid-1;
				continue;
			}
			node L,R;
			R=*it;it--;L=*it;
			if(R.y-L.y<=-a[u]*(R.x-L.x))l=mid+1;
			else r=mid-1;
		}
		it=st[who[u]].lower_bound(node(r,-inf));
		ans[u]=it->y+it->x*a[u];
	}
	insert(who[u],node(b[u],ans[u]));
}
int main()
{
	ll n;
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(ll i=1;i<=n;i++)
		scanf("%lld",&b[i]);
	for(ll i=0;i


你可能感兴趣的:(Codeforces,斜率优化,启发式合并)