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


你可能感兴趣的:(bzoj3672: [Noi2014]购票)