BZOJ 3672: [Noi2014]购票

近年来的趋势都是把动态规划出成计算几何吗?

这题首先我们有个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;
}
	
	


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