UOJ #7 NOI2014购票(点分治+cdq分治+斜率优化+动态规划)

  重写一遍很久以前写过的题。

  考虑链上的问题。容易想到设f[i]为i到1的最少购票费用,转移有f[i]=min{f[j]+(dep[i]-dep[j])*p[i]+q[i]} (dep[i]-dep[j]<=l[i])。套路的考虑若j转移优于k(dep[j]>dep[k]),则f[j]-dep[j]*p[i]<f[k]-dep[k]*p[i],f[j]-f[k]<(dep[j]-dep[k])*p[i],(f[j]-f[k])/(dep[j]-dep[k])<p[i]。若没有l[]的限制,对(dep[],f[])维护一个下凸壳即可。加入l[]的限制后,考虑使用cdq分治,右侧按dep[]-l[]从大到小排序,更新点时将新增的可以用来更新的点加入凸壳,并在凸壳上二分。

  拓展到树上,通过点分治实现一个树上cdq即可,同理要先处理靠近根的部分。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200010
#define int long long
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,p[N],t,fa[N],w[N],v[N],lim[N],deep[N],f[N],size[N],q[N],u[N],cnt2;
bool flag[N];
struct data{int to,nxt,len;
}edge[N<<1];
void addedge(int x,int y,int z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;}
void dfs(int k)
{
	for (int i=p[k];i;i=edge[i].nxt)
	if (edge[i].to!=fa[k])
	{
		deep[edge[i].to]=deep[k]+edge[i].len;
		dfs(edge[i].to);
	}
}
void make(int k,int from)
{
	size[k]=1;
	for (int i=p[k];i;i=edge[i].nxt) 
	if (edge[i].to!=from&&!flag[edge[i].to])
	{
		make(edge[i].to,k);
		size[k]+=size[edge[i].to];
	}
}
int findroot(int k,int from,int s)
{
	int mx=0;
	for (int i=p[k];i;i=edge[i].nxt) 
	if (edge[i].to!=from&&!flag[edge[i].to]&&size[edge[i].to]>size[mx]) mx=edge[i].to;
	if ((size[mx]<<1)>s) return findroot(mx,k,s);
	else return k;
}
void build(int k,int from,int *id,int &cnt)
{
	id[++cnt]=k;
	for (int i=p[k];i;i=edge[i].nxt)
	if (edge[i].to!=from&&!flag[edge[i].to]&&deep[edge[i].to]<deep[k]) build(edge[i].to,k,id,cnt);
}
long double slope(int x,int y){return (long double)(f[x]-f[y])/(deep[x]-deep[y]);}
void get(int k,int from)
{
	u[++cnt2]=k;
	for (int i=p[k];i;i=edge[i].nxt)
	if (edge[i].to!=from&&!flag[edge[i].to]) get(edge[i].to,k);
}
bool cmp(const int&a,const int&b)
{
	return deep[a]-lim[a]>deep[b]-lim[b];
}
void solve(int k)
{
	make(k,k);k=findroot(k,k,size[k]);flag[k]=1;
	int id[N],cnt;id[cnt=1]=k;
	for (int i=p[k];i;i=edge[i].nxt)
	if (!flag[edge[i].to]&&deep[edge[i].to]<deep[k])
	{
		build(edge[i].to,edge[i].to,id,cnt);
		solve(edge[i].to);
	}
	for (int i=2;i<=cnt;i++) if (deep[k]-deep[id[i]]<=lim[k]) f[k]=min(f[k],f[id[i]]+w[k]*(deep[k]-deep[id[i]])+v[k]);
	cnt2=0;
	for (int i=p[k];i;i=edge[i].nxt)
	if (!flag[edge[i].to]) get(edge[i].to,edge[i].to);
	sort(u+1,u+cnt2+1,cmp);
	int t=0,tail=0;
	for (int i=1;i<=cnt2;i++)
	{
		while (t<cnt&&deep[id[t+1]]>=deep[u[i]]-lim[u[i]])
		{
			while (tail>1&&slope(id[t+1],q[tail])>slope(q[tail],q[tail-1])) tail--;
			q[++tail]=id[++t];
		}
		if (tail)
		{
			int l=1,r=tail-1,ans=tail;
			while (l<=r)
			{
				int mid=l+r>>1;
				if (slope(q[mid+1],q[mid])<w[u[i]]) ans=mid,r=mid-1;
				else l=mid+1;
			}
			f[u[i]]=min(f[u[i]],f[q[ans]]+w[u[i]]*(deep[u[i]]-deep[q[ans]])+v[u[i]]);
		}
	}
	cnt=0;
	for (int i=p[k];i;i=edge[i].nxt)
	if (!flag[edge[i].to]) solve(edge[i].to);
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d\n";
#else
	const char LL[]="%lld\n";
#endif
	n=read();read();
	for (int i=2;i<=n;i++)
	{
		fa[i]=read();
		addedge(fa[i],i,read());
		addedge(i,fa[i],0);
		w[i]=read(),v[i]=read(),lim[i]=read();
	}
	dfs(1);
	memset(f,60,sizeof(f));f[1]=0;
	solve(1);
	for (int i=2;i<=n;i++) printf(LL,f[i]);
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/Gloid/p/10946513.html