近年来的趋势都是把动态规划出成计算几何吗?
这题首先我们有个n^2的动规
设v为u的祖先f[u]=min{f[v]+(d[u]-d[v])*p[u]+q[u]}且d[u]-d[v]<=l[u]
~~~~~我要变形了~~~~~~
f[u]=min{-d[v]*p[u]+f[v]}+d[u]*p[u]+q[u]
哎,前面这个好像什么东西啊
y=kx+b
于是我们发现u的祖先是好多线
假设p[u]为一个点的x坐标,该点在线v上,则其y坐标为-d[v]*p[u]+f[v]
于是我们要找的就是使y坐标最小的线v
若线w的在任意横坐标上的纵坐标都大于v,那么w不用考虑
所以问题可以转化为半平面交
于是这道题就是动态半平面交了,我们用可持久化平衡树就可以在O(玄学)的时间内做出来
假设我们已经有一个半平面和横坐标x,那么我们可以在O(logn)的时间内找到使得y最小的那条线
既然是树,那么我们可以考虑一下树链剖分
每条树链用一个线段树维护一下树链上各个区间半平面交的结果
由于从祖先到子孙的d[u]会递增,所以我们有一个天然的斜率顺序
所以这个可以用vector维护。
空间O(nlogn),时间O(n(logn)^3)
但是由于我是懒癌晚期,一写树剖就想剁手各种颓
所以我们还是来考虑一下点分治做法
众所周知我们有QDC治分
我们不妨把两个套一下
把当前分治结构的重心找到删去后,我们会发现只有包括根的那个分支对其他的有影响
于是我们把重心和连向根的那个分支一起先分治了
于是那个部分已经算出来了,我们考虑如何影响其他部分
既然其他部分都要更新,我们把它们拿出来,按照所能被更新的深度从大到小排序(即d-l),然后在更新的过程中维护从重心到根的半平面交。
最后再把其他分支都分治下去
由于我的点分治一向写得很挫所以写了140多行QAQ
再加上求最值的二分(疑似三分)没写过,调了一个小时TAT
感觉自己好弱啊还是去水HN的题吧
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int N=200000+5; typedef long long ll; const ll inf=1LL<<60; struct line{ ll k,b; }; struct city{ ll p,q,d,l,f; int fa; line li(){ return (line){-d,f}; } }c[N]; bool cmp(int i,int j){ return c[i].d-c[i].l>=c[j].d-c[j].l; } struct Edge{int to,next;ll v;}e[N<<1]; int head[N],cnt; void ins(int u,int v,ll w){ e[++cnt]=(Edge){v,head[u],w};head[u]=cnt; } void insert(int u,int v,ll w){ ins(u,v,w);ins(v,u,w); } bool del[N]; int getsize(int u,int fa){ int sz=1; for(int i=head[u];i;i=e[i].next){ int v=e[i].to;if(v==fa||del[v])continue; sz+=getsize(v,u); } return sz; } int getroot(int u,int fa,int size,int &root){ int sz=1,tmp;bool flag=true; for(int i=head[u];i;i=e[i].next){ int v=e[i].to;if(v==fa||del[v])continue; tmp=getroot(v,u,size,root); if(tmp<<1>size)flag=false; sz+=tmp; } if(size-sz<<1>size)flag=false; if(flag)root=u; return sz; } bool find(int u,int fa,int root){ if(u==root)return true; for(int i=head[u];i;i=e[i].next){ int v=e[i].to;if(v==fa||del[v])continue; if(find(v,u,root))return true; } return false; } int s1[N],tp1,s2[N],tp2,s[N],tp; void push(int u,int fa,ll d){ if(c[u].d-c[u].l<=d)s1[++tp1]=u; for(int i=head[u];i;i=e[i].next){ int v=e[i].to;if(v==fa||del[v])continue; push(v,u,d); } } bool check(line l1,line l2,line l){ return double(l.k-l1.k)/double(l1.k-l2.k)*double(l2.b-l1.b)<=double(l1.b-l.b); } bool mark[N]; ll gety(int t,ll x){ return c[t].f-c[t].d*x; } ll solve(ll x){ ll y=inf,ans1,ans2; int l=1,r=tp; while(l<r){ int mid=l+r>>1; ans1=gety(s[mid],x); ans2=gety(s[mid+1],x); if(ans1<ans2)r=mid,y=min(y,ans1); else l=mid+1,y=min(y,ans2); } for(int i=l;i<=r;i++)y=min(y,gety(s[i],x)); return y; } void reuqnoc_dna_edivid_dqc(int u){ int size=getsize(u,-1),root; if(size==1)return; getroot(u,-1,size,root); int vi=0; for(int i=head[root];i;i=e[i].next){ int v=e[i].to;if(del[v])continue; if(find(v,root,u))vi=v; else del[v]=mark[v]=true; } if(vi)reuqnoc_dna_edivid_dqc(u); tp1=0; for(int i=head[root];i;i=e[i].next){ int v=e[i].to; if(mark[v])mark[v]=del[v]=false; if(del[v]||v==vi)continue; push(v,root,c[root].d); } sort(s1+1,s1+1+tp1,cmp); tp2=0; for(int i=root;;i=c[i].fa){ s2[++tp2]=i; if(i==u)break; } int j=1; tp=0; for(int i=1;i<=tp1;i++){ while(j<=tp2&&c[s2[j]].d>=c[s1[i]].d-c[s1[i]].l){ while(tp>1&&check(c[s[tp-1]].li(),c[s[tp]].li(),c[s2[j]].li()))tp--; s[++tp]=s2[j++]; } c[s1[i]].f=min(c[s1[i]].f,solve(c[s1[i]].p)+c[s1[i]].p*c[s1[i]].d+c[s1[i]].q); } del[root]=true; for(int i=head[root];i;i=e[i].next){ int v=e[i].to;if(v==vi||del[v])continue; reuqnoc_dna_edivid_dqc(v); } } void dfs(int u,int fa){ for(int i=head[u];i;i=e[i].next){ int v=e[i].to;if(v==fa)continue; c[v].d=c[u].d+e[i].v; dfs(v,u); } } int main(){ //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); int n,t;scanf("%d%d",&n,&t); for(int i=2;i<=n;i++){ ll w; scanf("%d%lld%lld%lld%lld",&c[i].fa,&w,&c[i].p,&c[i].q,&c[i].l); insert(i,c[i].fa,w); c[i].f=inf; } dfs(1,-1); reuqnoc_dna_edivid_dqc(1); for(int i=2;i<=n;i++) printf("%lld\n",c[i].f); return 0; }