bzoj3672: [Noi2014]购票
传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3672
思路:思路:首先不考虑树,也不考虑距离限制,假设是链上且无距离限制。
设每个点到根的路径为d[i],两点之间路径长为dist(i,j)
那么DP方程很显然f[i]=min(f[j]+dist(i,j)*p[i]+q[i])(i>j)
f[i]=min(f[j]+d[i]*p[i]-d[j]*p[i]+q[i])
这个式子显然可以斜率优化
f[i]-d[i]*p[i]+d[j]*p[i]-q[i]=f[j]
f[j]=p[i]*d[j]-d[i]*p[i]-q[i]+f[i]
y =k x + b
那么我们只要让其截距最小即可
如果以(d[j],f[j])为坐标,那么我们就需要一个下凸壳上的点
用一个栈维护,每次二分斜率p[i]即可。
然后考虑怎么变成树上的。
用树链剖分,线段树维护下凸壳。
线段树每个节点是一个vector,维护的是这一段上的点构成的下凸壳的坐标。
询问就对每一段的下凸壳二分,取最小值即可
至于距离限制,在树形DP的时候开一个栈,到一个点就把它压进栈,它的子树DP完了就退栈。
这样栈里就按深度保存了根到当前点的路径上的所有点,二分最远距离,找到能到达的最远的点
对这一段区间进行询问即可。
时间复杂度O(n*log^3 n) 空间复杂度O(nlogn)
#include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define ls (p<<1) #define rs ((p<<1)|1) #define mid ((l+r)>>1) #define pb(a) push_back(a) #define pob() pop_back() typedef long long ll; const int maxm=200010,maxn=200010; const double eps=1e-11; const ll inf=4611686018427387903LL; using namespace std; int n,m,pre[maxm],now[maxn],son[maxm],tot,modpp,tim;ll val[maxm],lim[maxm],p[maxn],q[maxn],f[maxn],dis[maxn],stk[maxn]; int dep[maxn],top[maxn],siz[maxn],hson[maxn],fa[maxn],w[maxn],top1,num[maxn]; void add(int a,int b,ll c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;} struct node{ll x,y;int id;}; struct Tseg{ struct Tnode{ vector<node> stk; double slope(node a,node b){return a.x==b.x?(double)inf:1.0*(a.y-b.y)/(a.x-b.x);} ll getans(int i,int j){/*if (dis[i]<dis[j]) printf("fuckpp%lld %lld\n",dep[i],dep[j]);*/return f[j]+(dis[i]-dis[j])*p[i]+q[i];} void insert(node p){ int siz=stk.size(); while ((siz=stk.size())>1&&slope(stk[siz-1],stk[siz-2])>slope(stk[siz-1],p)) stk.pob(); stk.pb(p); } ll query(int id){ int siz=stk.size();ll res=inf; if (!siz) return inf; int l=1,r=siz;double k=(double)p[id]; //printf("%lld\n",p[id]); stk.push_back((node){stk[siz-1].x+1,inf,0});//防止二分时右边界出错 while (l<=r){ double lk=slope(stk[mid-1],stk[mid]),rk=slope(stk[mid],stk[mid+1]); //printf("%d %d %d %lld %lld\n",l,mid,r,stk[mid].x,stk[mid].y); //printf("%lld\n",f[1]); if (k+eps<lk) r=mid-1; else if (k>rk+eps) l=mid+1; else{res=getans(id,stk[mid].id);break;} } //printf("res%lld\n",res); stk.pob();return res; } }t[maxn<<2]; void build(ll p,ll l,ll r){ t[p].stk.push_back((node){-1,inf,0});//防止二分时左边界出错 if (l==r) return; build(ls,l,mid),build(rs,mid+1,r); } void insert(int p,int l,int r,int x,node pp){ t[p].insert(pp);if (l==r) return; if (x<=mid) insert(ls,l,mid,x,pp); else insert(rs,mid+1,r,x,pp); } ll query(int p,int l,int r,int a,int b,int id){ //if (p==1) printf("%d %d %d %d\n",l,r,a,b); if (l==a&&r==b){ //printf("query%d %d %d %d\n",l,r,a,b); return t[p].query(id);} if (b<=mid) return query(ls,l,mid,a,b,id); else if (a>mid) return query(rs,mid+1,r,a,b,id); else return min(query(ls,l,mid,a,mid,id),query(rs,mid+1,r,mid+1,b,id)); } void insert(int x,node t){insert(1,1,n,x,t);}; ll query(int l,int r,int k){return query(1,1,n,l,r,k);}; }T; void dfs1(int x){ siz[x]=1; for (int y=now[x];y;y=pre[y]){ dep[son[y]]=dep[x]+1,dis[son[y]]=dis[x]+val[y],dfs1(son[y]); if (siz[son[y]]>siz[hson[x]]) hson[x]=son[y]; siz[x]+=siz[son[y]]; } } void btree(int x,int tp){ w[x]=++tim,top[x]=tp; if (hson[x]) btree(hson[x],tp); for (ll y=now[x];y;y=pre[y]) if (son[y]!=hson[x]) btree(son[y],son[y]); } ll query(int a,int b){ int f1,f2,id=a;ll ans=inf; a=fa[a];f1=top[a],f2=top[b]; while (f1!=f2){ if (dep[f1]<dep[f2]) swap(a,b),swap(f1,f2); ans=min(ans,T.query(w[f1],w[a],id)); a=fa[f1],f1=top[a]; } if (w[a]>w[b]) swap(a,b); ans=min(ans,T.query(w[a],w[b],id)); return ans; } void dfs2(int x){ int anc=lower_bound(stk+1,stk+top1+1,dis[x]-lim[x])-stk; //printf("%d %d\n",x,num[anc]); if (x!=1) f[x]=query(x,num[anc]); T.insert(w[x],(node){dis[x],f[x],x}); stk[++top1]=dis[x],num[top1]=x; /*if (x==1){ printf("pp%d %lld %d\n",x,dis[x],num[top1]); for (ll i=1;i<=top1;i++) printf("modpp%d %lld %d\n",top1,stk[i],num[i]); }*/ for (int y=now[x];y;y=pre[y]) dfs2(son[y]); top1--; } int main(){ //freopen("ex_ticket2.in","r",stdin);//freopen("ex_ticket2.out","w",stdout); //stk[1]=(poi){1,0},stk[2]=(poi){2,1},stk[3]=(poi){3,2}; //printf("%d\n",lower_bound(stk+1,stk+4,(poi){1,0})-stk); scanf("%d%d",&n,&modpp);T.build(1,1,n); for (int i=2;i<=n;i++){ll c; scanf("%d%lld%lld%lld%lld",&fa[i],&c,&p[i],&q[i],&lim[i]),add(fa[i],i,c); //if (fa[i]>1000) fa[i]-=999;//if (i==161) printf("%lld\n",lim[i]); } dfs1(1),btree(1,1); //for (int i=1;i<=n;i++) printf("%lld %lld\n",i,dis[i]); dfs2(1); for (int i=2;i<=n;i++) printf("%lld\n",f[i]); return 0; }