zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",l,mid,r,stk[mid].x,stk[mid].y);
    				//printf("%lld
    ",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
    ",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
    ",l,r,a,b);
    		if (l==a&&r==b){
    			//printf("query%d %d %d %d
    ",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
    ",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
    ",x,dis[x],num[top1]);
    		for (ll i=1;i<=top1;i++) printf("modpp%d %lld %d
    ",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
    ",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
    ",lim[i]);
    	}
    	dfs1(1),btree(1,1);
    	//for (int i=1;i<=n;i++) printf("%lld %lld
    ",i,dis[i]);
    	dfs2(1);
    	for (int i=2;i<=n;i++) printf("%lld
    ",f[i]);
    	return 0;
    }
    


  • 相关阅读:
    Xshell 使用纪要
    矩阵求逆
    Ubuntu 增加新用户
    matlab 常用图像处理
    Surface Evolver 基本操作、使用指南和珍贵资料
    latex 裁剪图片
    Inkscape 输入希腊字母
    Pyton——int内部功能介绍
    python——登陆接口设计(循环方法)
    Python之三层菜单
  • 原文地址:https://www.cnblogs.com/thythy/p/5493500.html
Copyright © 2011-2022 走看看