• [NOI2014]购票


    题面在这里

    题意

    给出一棵大小为(n)的有根树,每个节点的父亲(fv)以及父边长(sv)
    每个节点给出一个距离限制(lv)。对于(v)的祖先(a)
    只有当它们之间所有道路的总长度不超过(lv)时,
    从城市(v)才可以通过一次购票到达城市(a),否则不能通过一次购票到达。
    对于每个城市(v),还给出两个非负整数(pv,qv)
    若城市(v)到城市(a)所有道路的总长度为(d)
    那么从城市(v)到城市(a)购买的票价为(d imes pv+qv)
    求每个节点到达根节点的最小代价

    数据范围

    [nle2 imes10^5 ]

    sol

    NOIday2压轴题。

    假设我们面对的只有一条链,那么设(f[i])表示第(i)号节点的答案,(s[i])为第(i)号节点到第(j)号点的距离,那么有

    [f[i]=min_{j=fa[i]}^{s[i]-s[j]le l[i]}(f[j]+(s[i]-s[j]) imes p[i]+q[i])]

    [=min_{j=fa[i]}^{s[i]-s[j]le l[i]}(f[j]-s[j] imes p[i])+s[i] imes p[i]+q[i] ]

    插点((s[j],f[j])),询问(k_i=p[i]);
    然后我们需要面对两个问题:

    树上操作

    这个需要维护一个可持久化单调队列,具体请看这里;

    处理横坐标连续的一些点的凸包

    使用单调队列的时候,直接弹出凸包肯定是不对的
    那么我们肯定需要数据结构来支持合并下凸包
    其实合并下凸包没有想象中那么难,
    直接把斜率在每个下凸包中取到的最值合并即可

    插入和回溯时我们每次需要更新线段树上的(O(logn))个节点,
    每个点最多在每个线段树节点上进入两次出去两次(可持久化),

    询问答案时我们每次二分距离是(O(logn))的,
    之后凸包上二分是(O(logn))的,线段树区间合并是(O(logn))的,
    所以总时间就是(O(n(logn+logn+logn+log^2n)))?(可能需要卡常)

    那么很明显这就是一道用数据结构维护的计算几何题(雾

    代码

    #include<bits/stdc++.h>
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<iomanip>
    #include<cstring>
    #include<complex>
    #include<vector>
    #include<cstdio>
    #include<string>
    #include<bitset>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<map>
    #include<set>
    #define mp make_pair
    #define pub push_back
    #define puf push_front
    #define pob pop_back
    #define pof pop_front
    #define RG register
    #define il inline
    using namespace std;
    typedef unsigned long long ull;
    typedef vector<int>VI;
    typedef long long ll;
    typedef double dd;
    const dd eps=1e-10;
    const int mod=1e8;
    const int N=200010;
    const ll inf=1e18;
    il ll read(){
    	RG ll data=0,w=1;RG char ch=getchar();
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
    	return data*w;
    }
    
    il void file(){
    	freopen("b.in","r",stdin);
    	freopen("a.out","w",stdout);
    }
    
    ll n,T,f[N],s[N],p[N],q[N],l[N];
    int head[N],nxt[N<<1],to[N<<1],cnt;
    ll val[N<<1];
    il void add(int u,int v,ll w){
    	to[++cnt]=v;
    	val[cnt]=w;
    	nxt[cnt]=head[u];
    	head[u]=cnt;
    }
    
    int cal[20][N],top[20];
    ll calx[20][N],caly[20][N];
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define mid ((l+r)>>1)
    il dd getk(ll ax,ll ay,ll bx,ll by){
    	if(ax==bx)return inf;return (ay-by)*1.0/(ax-bx);
    }
    struct node{
    	vector<ll> qx,qy;
    	il void init(){qx.clear();qy.clear();}
    	il int check(ll k,int x){
    		if(x!=qx.size()-1&&k>getk(qx[x+1],qy[x+1],qx[x],qy[x]))
    			return 1;
    		if(x&&k<getk(qx[x],qy[x],qx[x-1],qy[x-1]))
    			return -1;
    		return 0;
    	}
    	il ll solve(ll k){
    		RG int l=0,r=qx.size()-1,ret;
    		while(l<=r){
    			ret=check(k,mid);
    			if(!ret)return qy[mid]-k*qx[mid];
    			else if(ret==1)l=mid+1;
    			else r=mid-1;
    		}
    	}
    }t[20*N];
    
    il void build(int i,int l,int r){
    	t[i].init();if(l==r)return;
    	build(ls,l,mid);build(rs,mid+1,r);
    }
    
    il void modify(int i,ll x,ll y,int dep,int num){
    	RG int sz=t[i].qx.size();
    	while(sz>=2&&getk(t[i].qx[sz-1],t[i].qy[sz-1],t[i].qx[sz-2],t[i].qy[sz-2])>=getk(t[i].qx[sz-1],t[i].qy[sz-1],x,y)){
    		top[dep]++;
    		cal[dep][top[dep]]=num;
    		calx[dep][top[dep]]=t[i].qx[sz-1];
    		caly[dep][top[dep]]=t[i].qy[sz-1];
    		t[i].qx.pop_back();t[i].qy.pop_back();sz--;
    	}
    	t[i].qx.push_back(x);t[i].qy.push_back(y);
    }
    void inserttree(int i,int l,int r,int pos,ll x,ll y,int dep,int num){
    	modify(i,x,y,dep,num);
    	if(l==r)return;
    	if(pos<=mid)inserttree(ls,l,mid,pos,x,y,dep+1,num);
    	else inserttree(rs,mid+1,r,pos,x,y,dep+1,num);
    }
    
    ll querytree(int i,int l,int r,int x,int y,ll k){
    	if(x<=l&&r<=y)return t[i].solve(k);
    	RG ll s=inf;
    	if(x<=mid)s=querytree(ls,l,mid,x,y,k);
    	if(y>mid)s=min(s,querytree(rs,mid+1,r,x,y,k));
    	return s;
    }
    VI now;
    il ll query(ll dist,ll k){
    	RG int l=0,r=now.size()-1,ans;
    	while(l<=r){
    		if(s[now[mid]]>=dist)
    			ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	return querytree(1,1,n,ans+1,now.size()-1,k);
    }
    
    void canceltree(int i,int l,int r,int u,int pos,int dep){
    	if(l!=r){
    		if(pos<=mid)canceltree(ls,l,mid,u,pos,dep+1);
    		else canceltree(rs,mid+1,r,u,pos,dep+1);
    	}
    
    	t[i].qx.pop_back();t[i].qy.pop_back();
    	while(cal[dep][top[dep]]==u){
    		t[i].qx.push_back(calx[dep][top[dep]]);
    		t[i].qy.push_back(caly[dep][top[dep]]);
    		top[dep]--;
    	}
    	
    }
    
    void dfs_DP(int u,int fa){
    	now.push_back(u);
    	if(u!=1)f[u]=query(s[u]-l[u],p[u])+s[u]*p[u]+q[u];
    	inserttree(1,1,n,now.size(),s[u],f[u],1,u);
    	for(RG int i=head[u];i;i=nxt[i]){
    		RG int v=to[i];if(v==fa)continue;
    		s[v]=s[u]+val[i];dfs_DP(v,u);
    	}
    	canceltree(1,1,n,u,now.size(),1);now.pop_back();
    }
    
    int main()
    {
    	n=read();T=read();
    	for(RG int i=2,fa,w;i<=n;i++){
    		fa=read();w=read();add(i,fa,w);add(fa,i,w);
    		p[i]=read();q[i]=read();l[i]=read();
    	}
    	build(1,1,n);
    	dfs_DP(1,0);
    	for(RG int i=2;i<=n;i++)printf("%lld
    ",f[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    (三) 权限控制 --《springboot与shiro整合》
    (二) shiro集成 --《springboot与shiro整合》
    (一) springboot 项目搭建 --《springboot与shiro整合》
    第四章 编码/加密(学习笔记)
    第三章 授权(学习笔记)
    第二章 身份验证(学习笔记)
    获取小程序码java实现
    paypal退款 java实现
    并发下的数据处理和优化
    Casperjs初体验
  • 原文地址:https://www.cnblogs.com/cjfdf/p/8672879.html
Copyright © 2020-2023  润新知