BZOJ 3672 NOI2014 购票
这题好神啊..好神啊...好神啊...
首先列出N2的DP方程较易.
从DP方程很容易看出来是斜率优化.
如何进一步优化?
考虑对当前点以上的链建立一个下凸包.
维护凸包就可以,但不是很好写.
观察到方程可以必然由它的祖先节点转移.很像Cash那道题.
尝试CDQ分治,每次先递归处理根所在的子树.
然后用根所在的子树,对当前点更新答案,对其他点进行根据dis-lim排序,维护栈即可.
考虑到复杂度,我们需要对树进行点分治.
code:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<ctime> #include<string> #include<iomanip> #include<algorithm> #include<map> using namespace std; #define ll long long #define FILE "dealing" #define up(i,j,n) for(int i=j;i<=n;++i) #define db double #define uint unsigned ll #define eps 1e-12 #define pii pair<ll,ll> ll read(){ ll x=0,f=1,ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f*x; } const ll maxn=200500,limit=1e6,mod=(ll)(7+1e9+0.1); const ll inf=(ll)(1e18); template<class T>bool cmax(T& a,T b){return a<b?a=b,true:false;} template<class T>bool cmin(T& a,T b){return a>b?a=b,true:false;} template<class T>T min(T& a,T& b){return a<b?a:b;} template<class T>T max(T& a,T& b){return a>b?a:b;} int n,m; ll fa[maxn],p[maxn],q[maxn],lim[maxn],dis[maxn],dui[maxn],f[maxn]; struct node{ int y,next,v; }e[maxn]; int len,linkk[maxn]; void insert(int x,int y,ll v){ e[++len].y=y; e[len].v=v; e[len].next=linkk[x]; linkk[x]=len; } int root=0,siz[maxn],vis[maxn],Max[maxn],cnt; struct Node{ ll val,id; }a[maxn]; void dfs1(ll x){ siz[x]=1; for(ll i=linkk[x];i;i=e[i].next){ dis[e[i].y]=dis[x]+e[i].v; dfs1(e[i].y); siz[x]+=siz[e[i].y]; } } void find_root(ll x,int S,ll& rt){ Max[x]=0,siz[x]=1; for(ll i=linkk[x];i;i=e[i].next){ if(vis[e[i].y])continue; find_root(e[i].y,S,rt); siz[x]+=siz[e[i].y]; cmax(Max[x],siz[e[i].y]); } cmax(Max[x],S-siz[x]); if(Max[x]<Max[rt]&&siz[x]>1)rt=x; } void dfs2(ll x){ a[++cnt].val=dis[x]-lim[x]; a[cnt].id=x; for(ll i=linkk[x];i;i=e[i].next) if(!vis[e[i].y])dfs2(e[i].y); } bool cmp(Node a,Node b){ return a.val>b.val; } ll upd(ll x,ll y){return f[y]+(dis[x]-dis[y])*p[x]+q[x];} db K(ll x,ll y){return (1.0*f[x]-f[y])/(dis[x]-dis[y]);} void solve(ll x,int S){ if(S==1)return;ll rt=0,now=0; find_root(x,S,rt); for(ll i=linkk[rt];i;i=e[i].next) vis[e[i].y]=1; solve(x,S-siz[rt]+1); cnt=0; for(ll i=linkk[rt];i;i=e[i].next)dfs2(e[i].y); sort(a+1,a+cnt+1,cmp); now=rt; int tail=0,l,r,mid,pos; for(ll i=1;i<=cnt;i++){ while(now!=fa[x]&&dis[a[i].id]-lim[a[i].id]<=dis[now]){ while(tail>1&&K(dui[tail],now)>=K(dui[tail],dui[tail-1]))tail--; dui[++tail]=now;now=fa[now]; } if(tail>0){ l=1,r=tail,pos=1; while(l<=r){ mid=(l+r)>>1;if(mid==tail){pos=tail;break;} if(K(dui[mid],dui[mid+1])>=p[a[i].id])l=mid+1,pos=mid+1; else r=mid-1; } cmin(f[a[i].id],upd(a[i].id,dui[pos])); } } for(ll i=linkk[rt];i;i=e[i].next)solve(e[i].y,siz[e[i].y]); } int main(){ freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); n=read();ll d=read(); up(i,2,n){ ll y=read(),v=read(); fa[i]=y; insert(y,i,v); p[i]=read(),q[i]=read(); lim[i]=read(); } dfs1(1);Max[0]=n+1; up(i,2,n)f[i]=inf; solve(1,siz[1]); up(i,2,n)printf("%lld\n",f[i]); return 0; }