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

7 篇文章 0 订阅
1 篇文章 0 订阅

废话不多说,直接上方程。
dp[i]=min(dp[j]+(dis[i]dis[j])×p[i]+q[i])
考虑 x y的儿子,且 x y优:
dp[x]dis[x]p[i]<dp[y]dis[y]p[i]
T(x,y)=dp[x]dp[y]dis[x]dis[y]<p[i]
T(x,y)<p[i],xson[y] 时, x y优。
如同我前面题解那样,可以证明两两决策斜率单调减,又因为不等式右边不单调,我们二分查找一个 T(x,y)>p[i],T(y,z)<p[i] y <script type="math/tex" id="MathJax-Element-1978">y</script>是最优决策。
由于这是一颗树,我们不可能直接像一维那么用单调队列搞,所以就树分治喽。
对于一颗子树,我们找出它的重心root,先递归根在的那一颗子树,然后暴力算出root的dp值。
把root的孩子按照向上能够到达的点的距离从大到小排序,这样我们就可以保证只会一直往上往单调队列里面加点。
然后就没了。搞了我一天,代码能力弱啊。

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<climits>
#define X first
#define Y second
#define DB double
#define MP make_pair
#define LL long long
#define int LL
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF LONG_LONG_MAX/10
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
const int MAXN=5*1e5+10,MAXM=5*MAXN;
struct Civitatum{
    LL fa,l,p,q,s;
}city[MAXN];
LL O;
template<typename T>void Read(T& x)
{
    x=0;int flag=0,sgn=1;char c;
    while(c=getchar())
    {
        if(c=='-')sgn=-1;
        else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
        else if(flag)break;
    }
    x*=sgn;
}
int n,sum,first[MAXN],next[MAXM],to[MAXM],w[MAXM];
int cnt=0,e,rt=0;
LL dp[MAXN],dis[MAXN];
bool can[MAXM];
int Marx[MAXN]={0x3f3f3f3f},st[MAXN],top=0,sz[MAXN];
DB k[MAXN];
vector<int> nodes;
void add(LL u,LL v,LL A)
{
    next[e]=first[u];first[u]=e;to[e]=v;w[e]=A;can[e]=1;++e;
}
void DFS(int u)
{
    for(int i=first[u];i!=-1;i=next[i])
    {
        int v=to[i];
        if(v==city[u].fa)continue;
        dis[v]=dis[u]+w[i];
        DFS(v);
    }
}
void Root(int u)
{
    Marx[u]=0;sz[u]=1;
    for(int i=first[u];i!=-1;i=next[i])
        if(can[i])
        {
            int v=to[i];
            Root(v);
            sz[u]+=sz[v];
            Marx[u]=max(Marx[u],sz[v]);
        }
    Marx[u]=max(Marx[u],sum-sz[u]);
    if(Marx[u]<Marx[rt])rt=u;
}
DB slope(int x,int y)
{
    return (DB)(dp[x]-dp[y])/(DB)(dis[x]-dis[y]);
}
void getNodes(int u)
{
    for(int i=first[u];i!=-1;i=next[i])if(can[i])
    {
        int v=to[i];
        if(v==city[u].fa)continue;
        nodes.pb(v);
        getNodes(v);
    }
}
bool cmp(int x,int y)
{
    return dis[x]-city[x].l>dis[y]-city[y].l;
}
void Insert(int x)
{
    while((top>1)&&slope(st[top-1],st[top])<slope(st[top],x))
            top--;
    st[++top]=x;
    if(st[top]==5&&st[top-1]==4)
        st[top]=5;
    if(top>1)k[top]=slope(st[top-1],st[top]);
}
LL update(int i,int j)
{
    return dp[j]+(dis[i]-dis[j])*city[i].p+city[i].q;
}
int _find(int l,int r,DB p)
{
    if(top==1)return 1;
    if(k[l]<=p)return 1;
    if(k[r-1]>=p)return r-1;
    r--;
    int mid,R=r;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(l==r)
        {
            if(k[mid]>p)
                return mid;
            else
                return mid-1;
        }
        if(k[mid]>p&&(mid==R||k[mid+1]<=p))
            return mid;
        else if(k[mid+1]>p)
            l=mid;
        else
            r=mid;
    }
}
void work(int u,int id)
{   
    //DEBUG("%d %d\n",u,cnt);
    if(sum<=1)return;
    can[id]=top=rt=0;
    Root(u);
    int root=rt;
    for(int i=first[city[root].fa];i!=-1;i=next[i])
    {
        int v=to[i];
        if(v==root)
        {
            sum=sz[u]-sz[root];
            work(u,i);
            break;
        }
    }
    LL _dis=0,res=INF;
    int now=root;
    top=0;
    while(now!=u)
    {
        _dis+=city[now].s;
        if(_dis>city[root].l)break;
        now=city[now].fa;
        res=min((LL)res,dp[now]-dis[now]*city[root].p);
    }
    dp[root]=min(dp[root],res+dis[root]*city[root].p+city[root].q);
    nodes.clear();
    getNodes(root);
    if(!nodes.size())return;
    sort(nodes.begin(),nodes.end(),cmp);
    now=root,_dis=dis[nodes[0]]-dis[root];
    while(now!=city[u].fa)
    {
        if(_dis>city[nodes[0]].l)break;
        Insert(now);
        _dis+=city[now].s;
        now=city[now].fa;
    }
    int j;
    if(top)
    {
        j=_find(2,top+1,city[nodes[0]].p);
       if(j>top)j=1;
       dp[nodes[0]]=min(dp[nodes[0]],update(nodes[0],st[j]));
    }
    for(int i=1;i<nodes.size();i++)
    {
        int pre=nodes[i];
        while(dis[pre]-city[pre].l<=dis[now]&&now&&now!=city[u].fa)
            Insert(now),now=city[now].fa;
        if(!top)continue; 
        j=_find(2,top+1,(DB)city[pre].p);
        if(j>top)j=1;
        dp[pre]=min(dp[pre],update(pre,st[j]));
    }
    for(int i=first[root];i!=-1;i=next[i])
    {
O++;
        int v=to[i];
        if(can[i])
            sum=sz[v],work(v,i);
    }
}
main()
{
#ifndef ONLINE_JUDGE
    freopen("tick.in","r",stdin);
    freopen("tick.out","w",stdout);
#endif
    int t;
    Read(n);Read(t);
    memset(first,-1,sizeof(first));
    for(int i=0;i<=n;i++)
        dp[i]=INF;
    for(int i=2;i<=n;i++)
    {
        Read(city[i].fa);Read(city[i].s);
        Read(city[i].p);Read(city[i].q);Read(city[i].l);
        add(city[i].fa,i,city[i].s);
    }
    sum=n;
    dis[1]=0;
    dp[1]=0;
    DFS(1);
    work(1,2*n+1);
    for(int i=2;i<=n;i++)
        printf("%lld\n",dp[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值