BZOJ3672: [Noi2014]购票 树分治 斜率优化

http://www.lydsy.com/JudgeOnline/problem.php?id=3672
树上的CDQ分治。
和常规CDQ思路相同,一个点的可行决策点是从这个点往上连续一段,那么在分治过程中先递归重心往上的一块(包括重心这个点),再将其他点按可行深度排序,然后维护上面那些点的凸包来更新底下点的答案,最后分别递归底下的块即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<functional>
#include<algorithm>
#define gm 200001
using namespace std;
typedef long long ll;
int n,p[gm],fa[gm],t;
ll q[gm],l[gm],dep[gm];
bool ban[gm];
struct e
{
    int t;
    e *n;
    ll c;
    e(int t,e *n,const ll& c):t(t),n(n),c(c){}
}*f[gm];
void init(int x){for(e *i=f[x];i;i=i->n){dep[i->t]=dep[x]+i->c,init(i->t);}}
struct pnt
{
    ll x,y;
    pnt(const ll &x,const ll &y):x(x),y(y){}
    ll b(int k){return y-k*x;}
};
double slope(const pnt &a,const pnt &b){return (double)(a.y-b.y)/(a.x-b.x);}
struct hull
{
    vector<pnt> p;
    vector<double> d;
    int sz;
    hull():p(),d(),sz(){}
    ~hull()=default;
    void push_back(const pnt &a)
    {
        if(!sz)
        {
            p.push_back(a); d.push_back(1e100);
            sz=1;
        }
        else
        {
            while(sz>1&&slope(p[sz-1],a)>d[sz-1])
            {
                --sz; p.pop_back(); d.pop_back();
            }
            p.push_back(a); d.push_back(slope(p[sz-1],a));
            ++sz;
        }
    }
    ll find(const ll &k)
    {
        int pos=upper_bound(d.begin(),d.end(),(double)k,greater<double>())-d.begin()-1;
        return p[pos].b(k);
    }
    bool empty() {return !sz;}
};
ll dp[gm];
int sz[gm];
int lim,g;
void dfs(int x)
{
    bool is=1;
    sz[x]=1;
    for(e *i=f[x];i;i=i->n)
    {
        if(ban[i->t]) continue;
        dfs(i->t);
        if(sz[i->t]>lim>>1) is=0;
        sz[x]+=sz[i->t];
    }
    if(lim-sz[x]>lim>>1) is=0;
    if(is) g=x;
}
int r[gm],cnt;
void push(int x)
{
    r[++cnt]=x;
    for(e *i=f[x];i;i=i->n)
    {
        if(ban[i->t]) continue;
        push(i->t);
    }
}
struct cmp{bool operator() (int a,int b){return dep[a]-l[a]>dep[b]-l[b];}};
void solve(int rt,int tot)
{
    lim=tot; dfs(rt); int x=g;
    vector<int> lst;
    for(e *i=f[x];i;i=i->n)
    {
        if(ban[i->t]) continue;
        ban[i->t]=1; lst.push_back(i->t); tot-=sz[i->t];
    }
    if(rt!=x) solve(rt,tot);
    ban[x]=1;cnt=0;
    for(vector<int>::iterator it=lst.begin();it!=lst.end();++it){ban[*it]=0; push(*it);}
    sort(r+1,r+cnt+1,cmp());
    hull h;
    for(int i=1;i<=cnt;++i)
    {
        int y=r[i];
        while(x!=fa[rt]&&dep[x]>=dep[y]-l[y]) h.push_back(pnt(dep[x],dp[x])),x=fa[x];
        if(!h.empty())
        {
            ll kre=h.find(p[y]);
            dp[y]=min(dp[y],kre+p[y]*dep[y]+q[y]);
        }
    }
    for(vector<int>::iterator it=lst.begin();it!=lst.end();++it){solve(*it,sz[*it]);}
}
int main()
{
    scanf("%d%d",&n,&t);
    memset(dp+1,0x7f,n<<3); dp[1]=0;
    for(int i=2;i<=n;++i)
    {
        ll len;
        scanf("%d%lld%d%lld%lld",fa+i,&len,p+i,q+i,l+i);
        f[fa[i]]=new e(i,f[fa[i]],len);
    }
    init(1);
    solve(1,n);
    for(int i=2;i<=n;++i) printf("%lld\n",dp[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值