zoukankan      html  css  js  c++  java
  • [NOI2014]购票

    题目

    读懂题目之后能写出一个dp方程,(dp_i=dp_j+(d_i-d_j)p_i+q_i(d_i-d_jleq lim_i)),其中(d_i)是根路径前缀和

    不难发现这个东西长得像斜率优化,需要建个凸壳来搞一搞;不难想到一个树剖+线段树维护的无脑做法,是(O(nlog^3n))的,看起来和暴力差不多;

    考虑有脑做法————有根树点分治,假设我们当前在处理一棵以(x)为根的有根树,流程大概长这个样子

    • 找到重心(nw)

    • 将重心与其儿子断开,递归处理重心所在联通块(当然,根也在这个联通块中)

    • 考虑(nw)(x)路径上的点对(nw)子树内部点产生的影响

    • 递归处理(nw)的子树

    在这道题中,我们将重心子树中的点都搞出来,按照(lim_i -dis_i)从小到大排序,(dis_i)是点(i)到根的距离;之后把重心到当前根上的点拿出来,按照距离排序;开个指针扫,把符合条件的点加入下凸壳即可,复查询的时候直接二分,复杂度是(O(nlog^2n))

    代码

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline LL read() {
    	char c=getchar();LL x=0;while(c<'0'||c>'9')c=getchar();
    	while(c>='0'&&c<='9')x=10ll*x+c-48,c=getchar();return x;
    }
    const double eps=1e-8;const int maxn=2e5+5;
    struct E{int v,nxt;}e[maxn];
    int head[maxn],fa[maxn],S,mx[maxn],rt,vis[maxn],n;
    int lp,top,st[maxn],cnt,bl[maxn],num,nw,sk[maxn],sum[maxn];
    LL p[maxn],q[maxn],dis[maxn],lm[maxn],w[maxn],dp[maxn];
    inline void add(int x,int y) {
    	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
    }
    void getrt(int x) {
    	sum[x]=1;mx[x]=0;
    	for(re int i=head[x];i;i=e[i].nxt) {
    		if(vis[i])continue;getrt(e[i].v);
    		sum[x]+=sum[e[i].v];mx[x]=max(mx[x],sum[e[i].v]);
    	}
    	mx[x]=max(mx[x],S-sum[x]);if(mx[x]<mx[rt])rt=x;
    }
    void getdis(int x) {
    	st[++top]=x;
    	for(re int i=head[x];i;i=e[i].nxt) 
    		if(!vis[i])dis[e[i].v]=dis[x]+w[e[i].v],getdis(e[i].v);
    }
    inline int cop(int A,int B) {return dis[A]<dis[B];}
    inline int cmp(int A,int B) {return lm[A]-dis[A]<lm[B]-dis[B];}
    inline int dcmp(double a,double b){return a+eps>b&&a-eps<b;}
    inline double slope(int a,int b) {
    	return (double)(dp[b]-dp[a])/(double)(dis[b]-dis[a]);
    }
    inline void ins(int x) {
    	if(nw<1) {sk[++nw]=x;return;}
    	while(nw>1&&slope(sk[nw-1],x)<slope(sk[nw-1],sk[nw]))--nw;
    	sk[++nw]=x;
    }
    inline int fid(LL k) {
    	if(nw<=1)return sk[nw];
    	if(slope(sk[nw-1],sk[nw])<k) return sk[nw];
    	int l=1,r=nw-1,h=1;
    	while(l<=r) {
    		int mid=l+r>>1;double K=slope(sk[mid],sk[mid+1]);
    		if(K>k||dcmp(K,k))h=mid,r=mid-1;
    		else l=mid+1;
    	}
    	return sk[h];
    }
    void solve(int x,int nsz) {
    	if(nsz==1)return;
    	S=nsz;rt=0;getrt(x);if(nsz==2)rt=x;int t=rt,h=rt;
    	for(re int i=head[rt];i;i=e[i].nxt)vis[i]=1,nsz-=sum[e[i].v];
    	solve(x,nsz);dis[t]=0;top=0;cnt=1;bl[cnt]=x;lp=1;nw=0;
    	for(re int i=head[t];i;i=e[i].nxt) 
    		dis[e[i].v]=w[e[i].v],getdis(e[i].v);
    	while(t!=x) 
    		bl[++cnt]=t,dis[fa[t]]=dis[t]+w[t],t=fa[t];
    	std::sort(st+1,st+top+1,cmp);
    	std::sort(bl+1,bl+cnt+1,cop);
    	for(re int i=1;i<=top;i++) {
    		while(lp<=cnt&&dis[bl[lp]]+dis[st[i]]<=lm[st[i]])ins(bl[lp]),++lp;
    		int j=fid(-1ll*p[st[i]]);
    		LL k=dp[j]+(dis[st[i]]+dis[j])*p[st[i]]+q[st[i]];
    		if(j)dp[st[i]]=min(dp[st[i]],k);
    	}
    	for(re int i=head[h];i;i=e[i].nxt)solve(e[i].v,sum[e[i].v]);
    }
    int main() {
    	n=read(),read();
    	for(re int i=2;i<=n;i++) {
    		fa[i]=read(),add(fa[i],i);w[i]=read();
    		p[i]=read(),q[i]=read(),lm[i]=read();
    	}
    	dp[1]=0;for(re int i=2;i<=n;i++)dp[i]=1e18;
    	mx[0]=n+1;solve(1,n);
    	for(re int i=2;i<=n;i++)printf("%lld
    ",dp[i]);
    	return 0;
    }
    
  • 相关阅读:
    Android开发 使用 adb logcat 显示 Android 日志
    【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410
    C语言 结构体相关 函数 指针 数组
    C语言 命令行参数 函数指针 gdb调试
    C语言 指针数组 多维数组
    Ubuntu 基础操作 基础命令 热键 man手册使用 关机 重启等命令使用
    C语言 内存分配 地址 指针 数组 参数 实例解析
    CRT 环境变量注意事项
    hadoop 输出文件 key val 分隔符
    com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: Too many connections
  • 原文地址:https://www.cnblogs.com/asuldb/p/12250118.html
Copyright © 2011-2022 走看看