[Noi2014]购票 斜率优化DP+可持久化凸包

貌似网上大部分题解都是CDQ分治+点分治然后再斜率优化DP,我貌似并没有用这个方法。

这一题跟这题有点像,只不过多了一个l的限制

如果说直接跑斜率优化DP,存储整个序列的话,显然是不行的,如图所示(图鸣谢某巨佬

 

所以我们需要种一棵线段树,每个线段树内存储一个存当前区间凸包的单调栈,弹出插入操作跟刚刚说的那题一样。

查询的话就查询下整个区间中所有凸包上的最大值就可以了。

时间复杂度:$O(n\log^2\ n)$。写起来并不算很困难。

 

  1 #include<bits/stdc++.h>
  2 #define M 200005
  3 #define L long long
  4 #define INF (1LL<<62)
  5 using namespace std;
  6 
  7 struct edge{int u,v,next;}e[M]={0}; int head[M]={0},use=0;
  8 void add(int x,int y,int z){use++;e[use].u=y;e[use].v=z;e[use].next=head[x];head[x]=use;}
  9 L D[M]={0},P[M]={0},Q[M]={0},F[M]={0},n,t,up[M]={0};
 10 double slope(int i,int j){return 1.*(F[i]-F[j])/(D[i]-D[j]);}
 11 L getans(int x,int y){if(y==0) return INF; return F[y]+(D[x]-D[y])*P[x]+Q[x];}
 12 
 13 int f[M][20]={0},dep[M]={0};
 14 int jump(int x,L dis){
 15     for(int i=19;~i;i--)
 16     if(D[x]-D[f[x][i]]<=dis){
 17         dis-=D[x]-D[f[x][i]];
 18         x=f[x][i];
 19     }
 20     return max(1,dep[x]);
 21 }
 22 
 23 struct tb{
 24     int *q,l,r;
 25     tb(){l=1; r=0; q=NULL;}
 26     tb(int len){
 27         q=new int[len+5];
 28         memset(q,0,sizeof(int )*(len+5));
 29         l=1; r=0;
 30     }
 31     int add(int x){
 32         int ll=l,rr=r-1;
 33         if(l<r){
 34             while(ll<rr){
 35                 int mid=(ll+rr)>>1;
 36                 if(slope(q[mid],q[mid+1])>slope(q[mid+1],x)) rr=mid;
 37                 else ll=mid+1;
 38             }
 39             if(slope(q[ll],q[ll+1])<slope(q[ll+1],x)) ll++;
 40             r=ll;
 41         }
 42         int res=q[++r]; q[r]=x;
 43         return res;
 44     }
 45     L getans(int x){
 46         int ll=l,rr=r-1;
 47         if(l>=r) return q[l];
 48         while(ll<rr){
 49             int mid=(ll+rr+1)>>1;
 50             if(slope(q[mid],q[mid+1])<P[x]) ll=mid;
 51             else rr=mid-1;
 52         }
 53         if(slope(q[ll],q[ll+1])<P[x]) 
 54         return q[ll+1];
 55         return q[ll];
 56     }
 57 };
 58 
 59 struct node{int l,r;tb a;}a[M*3];
 60 void build(int x,int l,int r){
 61     a[x].l=l; a[x].r=r;
 62     a[x].a=tb(r-l+1);
 63     if(l==r) return; int mid=(l+r)>>1;
 64     build(x<<1,l,mid); build(x<<1|1,mid+1,r);
 65 }
 66 void updata(int x,int k,int id,int lastL[],int lastR[],int lastT[]){
 67     lastL[0]=a[x].a.l; lastR[0]=a[x].a.r;
 68     lastT[0]=a[x].a.add(id);
 69     if(a[x].l==a[x].r) return; int mid=(a[x].l+a[x].r)>>1;
 70     if(k<=mid) updata(x<<1,k,id,lastL+1,lastR+1,lastT+1);
 71     else updata(x<<1|1,k,id,lastL+1,lastR+1,lastT+1);
 72 }
 73 void reset(int x,int k,int lastL[],int lastR[],int lastT[]){
 74     a[x].a.q[a[x].a.r]=lastT[0];
 75     a[x].a.l=lastL[0]; a[x].a.r=lastR[0];
 76     if(a[x].l==a[x].r) return; int mid=(a[x].l+a[x].r)>>1;
 77     if(k<=mid) reset(x<<1,k,lastL+1,lastR+1,lastT+1);
 78     else reset(x<<1|1,k,lastL+1,lastR+1,lastT+1);
 79 }
 80 L query(int x,int l,int r,int k){
 81     if(l<=a[x].l&&a[x].r<=r) 
 82     return getans(k,a[x].a.getans(k));
 83     L mid=(a[x].l+a[x].r)>>1,minn=INF;
 84     if(l<=mid) minn=min(minn,query(x<<1,l,r,k));
 85     if(mid<r) minn=min(minn,query(x<<1|1,l,r,k));
 86     return minn;
 87 }
 88 
 89 void dfs(int x,int fa,L Dis){
 90     f[x][0]=fa; dep[x]=dep[fa]+1; D[x]=Dis;
 91     for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
 92     int y=jump(x,up[x]);
 93     F[x]=query(1,y,dep[x],x);
 94     int lastL[20]={0},lastR[20]={0},lastT[20]={0};
 95     updata(1,dep[x],x,lastL,lastR,lastT);
 96     for(int i=head[x];i;i=e[i].next) dfs(e[i].u,x,Dis+e[i].v);
 97     reset(1,dep[x],lastL,lastR,lastT);
 98 }
 99 int main(){
100     scanf("%d%d",&n,&t);
101     for(int i=2;i<=n;i++){
102         L fa,dis; scanf("%lld%lld%lld%lld%lld",&fa,&dis,P+i,Q+i,up+i);
103         add(fa,i,dis);
104     }
105     build(1,1,n);
106     int hh[20]; updata(1,1,1,hh,hh,hh);
107     dep[1]=1; D[0]=-INF; 
108     for(int i=head[1];i;i=e[i].next) dfs(e[i].u,1,e[i].v);
109     for(int i=2;i<=n;i++) printf("%lld\n",F[i]);
110 }

转载于:https://www.cnblogs.com/xiefengze1/p/10425474.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值