1767: [Ceoi2009]harbingers 斜率优化

Description
给定一颗树,树中每个结点有一个邮递员,每个邮递员要沿着唯一的路径走向capital(1号结点),每到一个城市他可以有两种选择: 1.继续走到下个城市 2.让这个城市的邮递员替他出发。 每个邮递员出发需要一个准备时间W[I],他们的速度是V[I],表示走一公里需要多少分钟。 现在要你求出每个城市的邮递员到capital的最少时间(不一定是他自己到capital,可以是别人帮他) N<=100000 3 ≤ N ≤ 100 000 0 ≤ Si≤ 10^9 1 ≤ Vi≤ 10^9 The length of each road will not exceed 10 000 For 20% of the tests, N ≤ 2 500 For 50% of the tests, each town will have at most 2 adjacent roads (i.e., the graph of roads will be a line)

题解:

很容易写出DP方程: f [ x ] = m i n { f [ y ] + v [ x ] × ( d i s [ x ] d i s [ y ] ) + w [ x ] } y x 的祖先,然后用斜率优化的方法推一下式子,会发现这个方程的斜率并不是单调的,这就意味着我们不能简单地用一个单调队列来维护了,因为不能轻易地删除队头,这时我们可以用单调栈,寻找最优决策点时可以在上面二分。因为这是一棵树,所以我们还需要保证到达 x 点时,栈中的点是 x 到根节点上的点,所以我们在把 x 加入栈中的时候也要在栈中二分,把那个位置改为 x ,结束后再改回来,这样可以保证上面这点,具体可以看代码。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n;
struct Edge{int y,next;LL d;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y,LL d)
{
    int t=++len;
    e[t].y=y;e[t].d=d;
    e[t].next=last[x];last[x]=t;
}
LL f[Maxn],v[Maxn],w[Maxn],dis[Maxn];
int sta[Maxn],top=0;
double slope(int i,int j)
{
    if(!i)return 0;
    if(j==top+1)return (double)inf;
    i=sta[i],j=sta[j];
    return (double)(f[i]-f[j])/(dis[i]-dis[j]);
}
int find1(double k)
{
    if(!top)return 0;
    int l=1,r=top;
    while(l<=r)
    {
        int mid=l+r>>1;
        double lk=slope(mid-1,mid),rk=slope(mid,mid+1);
        if(lk<=k&&k<=rk)return sta[mid];
        if(lk<=k)l=mid+1;
        else r=mid-1;
    }
}
int find2(int x)
{
    if(!top)return 0;
    int l=1,r=top;
    while(l<=r)
    {
        int mid=l+r>>1;
        double lk=slope(mid-1,mid),rk=slope(mid,mid+1),k=(double)(f[sta[mid]]-f[x])/(dis[sta[mid]]-dis[x]);
        if(lk<=k&&k<=rk)return mid;
        if(k>rk)l=mid+1;
        else r=mid-1;
    }
}
void dfs(int x,int fa)
{
    if(x!=1)
    {
        int p=find1((double)v[x]);
        f[x]=f[p]+v[x]*(dis[x]-dis[p])+w[x];
    }
    int tmp=find2(x);
    int t1=sta[tmp+1],t2=top;
    top=tmp+1;sta[top]=x;
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(y==fa)continue;
        dis[y]=dis[x]+e[i].d;
        dfs(y,x);
    }
    sta[tmp+1]=t1,top=t2;
}
int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read(),d=read();
        ins(x,y,(LL)d),ins(y,x,(LL)d);
    }
    for(int i=2;i<=n;i++)w[i]=read(),v[i]=read();
    dis[1]=f[1]=0;dfs(1,0);
    for(int i=2;i<=n;i++)printf("%lld ",f[i]);
}

猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/79918073