当前位置: 首页 > news >正文

[NOI2014]购票

不错的一道码农题。

首先是个dp很显然了。

然后展开发现是个斜率优化不错。

但是取值有范围的。。。。

直接单调队列并不可以,因为凸包这个东西不是独立贡献答案的。

方法比较多,,

 

法一:

直观的想法是,既然可以转移的是一个区间,那么我们能不能快速得到这个区间的凸包呢?

线段树维护凸包应运而生。

只要在所有拆出来的区间中,分别在其维护的凸包上二分斜率即可。所有的值取min

但是如果dfs顺序来做的话,必须要支持凸包的撤销操作,,,,但是凸包的复杂度是均摊O(1)的,,,可以被特殊构造卡到O(n)。。。。

所以考虑能不能避免撤销操作。

还有没有什么可以动态处理树链的方法呢?

似乎只有树链剖分了。这样用dfn序维护整颗树即可。

 

修改:

如果处理好一个点的答案,

dfs询问处理的时候,也按照dfn序处理,这样就是不断在线段树最后加入一个点了。

凸包怎么办?可以暴力修改线段树一个链上的凸包,但是要对dis二分,而且常数极大。。

考虑到,每次询问都是一个之前dfn的区间,所以,当处理了1~3的dfn序之后,代表1~4的区间一定不用来询问。

所以,当一个区间的最后一个点加入之后,暴力把两个儿子合并即可。这样总线段树凸包的总复杂度O(nlogn)

询问:

树链剖分跳重链,把重链上有关的区间内的凸包上二分一下。注意lv[x]的边界处理。我是在树链剖分的时候额外二分了一下(其实线段树再记录min,max也可以)

O(nlog^3n)其中,树剖的logn不满,二分的logn更虚,所以实际跑的不错。

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
#define int long long
#define mid ((l+r)>>1)
#define ls (x<<1)
#define rs (x<<1|1)
#define fi first
#define se second
using namespace std;
typedef long long ll;
il void rd(ll &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=2e5+5;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n,m;
int dfn[N],df,fdfn[N],sz[N];
int dep[N];
int fa[N];
ll dis[N];
struct edge{
    int nxt,to;
    ll val;
}e[2*N];
int hd[N],cnt;
ll pv[N],qv[N],lv[N];
int top[N],son[N];
void add(int x,int y,ll z){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].val=z;
    hd[x]=cnt;
}
ll ans[N];
void dfs1(int x,int d){
    sz[x]=1;
    dep[x]=d;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        dis[y]=dis[x]+e[i].val;
        dfs1(y,d+1);
        sz[x]+=sz[y];
        if(sz[y]>sz[son[x]]) son[x]=y;
    }
}
void dfs2(int x){
    dfn[x]=++df;fdfn[df]=x;
    if(!top[x]) top[x]=x;
    if(son[x]) top[son[x]]=top[x],dfs2(son[x]);
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==son[x]) continue;
        dfs2(y);
    }
}
struct node{
    vector<pair<ll,ll> >tu;
    ll mi;//mindis 
}t[4*N];
int b[N],tot;
double cross(ll x1,ll y1,ll x2,ll y2){
    return (double)x1*y2-(double)x2*y1;
}
void pop(int x,ll d,ll f){
    while(t[x].tu.size()>1){
        int size=t[x].tu.size();
    //    cout<<" size "<<endl;
        double tmp=cross(t[x].tu[size-1].fi-d,t[x].tu[size-1].se-f,t[x].tu[size-2].fi-d,t[x].tu[size-2].se-f);
        if(tmp>=0) t[x].tu.pop_back();
        else break;
    }
    t[x].tu.push_back(make_pair(d,f));
}
void pushup(int x){
//    cout<<" pushing "<<x<<endl;
    t[x].mi=min(t[ls].mi,t[rs].mi);
    int le=0,ri=0;
    for(int i=1;i<=(int)(t[x<<1].tu.size())+(int)(t[x<<1|1].tu.size());++i){
    //    cout<<" ii "<<i<<" ---------------- "<<endl;
        if(le==(int)t[x<<1].tu.size()){
        //    cout<<" here "<<endl;
            pop(x,t[rs].tu[ri].fi,t[rs].tu[ri].se);++ri;                                                                                                                                      
        }else if(ri==(int)t[x<<1|1].tu.size()){
            pop(x,t[ls].tu[le].fi,t[ls].tu[le].se);++le;
        }
        else if(t[ls].tu[le].fi==t[rs].tu[ri].fi){//warning!!!
            if(t[ls].tu[le].se<t[rs].tu[ri].se) pop(x,t[ls].tu[le].fi,t[ls].tu[le].se);//,++le;
            else pop(x,t[rs].tu[ri].fi,t[rs].tu[ri].se);//,++ri;
            ++le;++ri;++i;
        }
        else if(t[ls].tu[le].fi<t[rs].tu[ri].fi){
            pop(x,t[ls].tu[le].fi,t[ls].tu[le].se);++le;    
        }else{
            pop(x,t[rs].tu[ri].fi,t[rs].tu[ri].se);++ri;
        }
    }
}
void upda(int x,int l,int r,int p,ll d,ll f){
//    cout<<" updaing "<<p<<" "<<d<<" "<<f<<endl;
    if(l==r){
        t[x].mi=d;
        t[x].tu.push_back(make_pair(d,f));
        return;
    }
    if(p<=mid) upda(x<<1,l,mid,p,d,f);
    else upda(x<<1|1,mid+1,r,p,d,f);
    if(p==r) pushup(x);
}
double slope(ll x1,ll y1,ll x2,ll y2){
    if(x2==x1){
        if(y1>y2) return inf;
        return -inf;
    }
    return ((double)y1-(double)y2)/((double)x1-(double)x2);
}
ll query(int x,int l,int r,int L,int R,ll k){//dis>=c        // return f[j]-dis[j]*k
    if(L<=l&&r<=R){
        ll ret=0;
//        if(t[x].mi>=c){
//            
//        }else{
//            ret=query(x<<1,l,mid,L,R,c,k);
//            ret=min(ret,query(x<<1|1,))
//        }
        int le=0,ri=t[x].tu.size()-1;
        while(le<=ri){
            int md=(le+ri)>>1;
            if(md==0){
                if(le==ri) ret=0;
                else{
                    ll t0=t[x].tu[0].se-t[x].tu[0].fi*k;
                    ll t1=t[x].tu[1].se-t[x].tu[1].fi*k;
                    if(t0<t1) ret=0;
                    else ret=1;
                }
                break;
            }
            else{
                double sl=slope(t[x].tu[md].fi,t[x].tu[md].se,t[x].tu[md-1].fi,t[x].tu[md-1].se);
                if(sl<=(double)k) ret=md,le=md+1;
                else ri=md-1; 
            }
        }
    //    cout<<" sz "<<t[x].tu.size()<<endl;
    //    cout<<" ret "<<ret<<endl;
        return t[x].tu[ret].se-t[x].tu[ret].fi*k;
    }
    ll ret=inf;
    if(L<=mid) ret=min(ret,query(x<<1,l,mid,L,R,k));
    if(mid<R) ret=min(ret,query(x<<1|1,mid+1,r,L,R,k));
    return ret;
}
void wrk(int x){
    //cout<<" wrking "<<x<<endl;
    int y=fa[x];
    ans[x]=inf;
    while(1){
        //cout<<" yy "<<y<<endl;
        if(dis[x]-dis[y]>lv[x]) break;
        if(!y) break;
        if(dis[x]-dis[top[y]]<=lv[x]){
            //cout<<" goto top "<<endl;
            ans[x]=min(ans[x],query(1,1,n,dfn[top[y]],dfn[y],pv[x]));
        }
        else{
            int l=dfn[top[y]],r=dfn[y];
            int up=y;
            while(l<=r){
                if(dis[x]-dis[fdfn[mid]]<=lv[x]) up=fdfn[mid],r=mid-1;
                else l=mid+1;
            }
            ///cout<<" up "<<up<<endl<<" pv "<<pv[x]<<endl;
            ans[x]=min(ans[x],query(1,1,n,dfn[up],dfn[y],pv[x]));
        }
        y=fa[top[y]];
    }
    //cout<<" ans[x] "<<ans[x]<<endl;
    ans[x]+=qv[x]+dis[x]*pv[x];
}
void dfs3(int x){
//    cout<<" sloving "<<x<<endl;
    if(x==1){
        upda(1,1,n,1,0,0);
    }    
    else{
        wrk(x);
    //    cout<<" after wrk "<<ans[x]<<endl;
        upda(1,1,n,dfn[x],dis[x],ans[x]);
    //    cout<<" upda finished "<<endl;
    }
    if(son[x]) dfs3(son[x]);
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==son[x]) continue;
        dfs3(y);
    }
}
int main(){
    rd(n);rd(m);
    int s;
    for(reg i=2;i<=n;++i){
        rd(fa[i]);rd(s);rd(pv[i]);rd(qv[i]);rd(lv[i]);
        add(fa[i],i,s);
    }
    dis[0]=0;
    dfs1(1,1);
    dfs2(1);
//    for(reg i=1;i<=n;++i){
//        cout<<i<<" : "<<dfn[i]<<" "<<fa[i]<<" "<<top[i]<<endl;
//    }
    dfs3(1);
    for(reg i=2;i<=n;++i){
        printf("%lld\n",ans[i]);
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/20 8:00:53
*/
View Code

 

 

法二:

这个跳树链实在还是多一个logn比较吃亏

如果可以dfs顺序来做就好了。这样直接一条链下来,凸包上直接二分就好。

上面已经分析,这样不能撤销。

树上求链还有什么算法呢?

点分治!

我们找到当前的重心H,先把H和根相连的部分和根的其他子树递归下去。

,,把和根相连的部分的这条链找出来,建一个凸包。

H的所有儿子的值也许都可以在这个凸包上找到决策点,dfs这些儿子的子树然后在凸包上二分

递归H的子树部分联通块。

这样,按顺序地,我们可以得到某个点的所有可能决策点的转移。

(其实思想有点类似CDQ分治,考虑H到根的路径对后面分治树部分的影响。)

理论O(nlog^2n) 二分的logn同样比较虚。

 

 

法三:

dfs真的不能直接进行吗?

其实可以。

一个厉害的东西是二进制分组。

至于撤销,

类似分块的处理,

如果多了4个1,那么把前两个1合成2,

如果多了4个2,那么把前两个2合成4

。。。

删除暴力干掉这个凸包

这样,每添加2^k个点,才会建造一个2^k的凸包。

每删除2^k个点,才会删除一个2^k的凸包。

所以,凸包的建造复杂度,基本和O(nlogn)同级。

就是往前面的暴力找常数比较大。

复杂度O(nlog^2n)+大常数。

 

转载于:https://www.cnblogs.com/Miracevin/p/10149101.html

相关文章:

  • iView动态生成Menu时open-names不生效的解决办法
  • Deepin怎样安装C/C++编译环境更好
  • Django学习【补充篇】:Django之MOdel进阶(QuerySet介绍以及这整体插入,中介模型等)...
  • Angular 应用解决跨域访问的问题
  • 用navicat远程连接mysql:Can't connect to MySQL server (10060)
  • 工具-cloc代码行数统计工具
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • GoAccess中文界面显示配置
  • Phpstorm Alt+Enter 自动导入类
  • 让你的系统“坚挺不倒”的最后一个大招——「降级」
  • Arch Linux开启SSH远程安装(1.5)
  • JVM调优后续(一)
  • ES6模块之export和import教程
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • ERLANG 网工修炼笔记 ---- UDP
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【面试系列】之二:关于js原型
  • AWS实战 - 利用IAM对S3做访问控制
  • ES6之路之模块详解
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • Joomla 2.x, 3.x useful code cheatsheet
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • Logstash 参考指南(目录)
  • php ci框架整合银盛支付
  • React-flux杂记
  • react-native 安卓真机环境搭建
  • Shadow DOM 内部构造及如何构建独立组件
  • SpringBoot几种定时任务的实现方式
  • vue总结
  • windows下使用nginx调试简介
  • 如何用vue打造一个移动端音乐播放器
  • 字符串匹配基础上
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • ​flutter 代码混淆
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #pragma multi_compile #pragma shader_feature
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (1)(1.9) MSP (version 4.2)
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (solr系列:一)使用tomcat部署solr服务
  • (zhuan) 一些RL的文献(及笔记)
  • (简单) HDU 2612 Find a way,BFS。
  • (论文阅读11/100)Fast R-CNN
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (三) diretfbrc详解
  • (十三)Maven插件解析运行机制
  • (四)图像的%2线性拉伸
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .NET Core 中插件式开发实现
  • .NET Framework 服务实现监控可观测性最佳实践
  • .NET MVC 验证码
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端