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;
}

  

posted @ 2017-03-13 18:09  CHADLZX  阅读(108)  评论(0编辑  收藏  举报