• 高速公路


    LXXVI.高速公路

    简直恶心到爆炸……

    首先,暴力的DP是非常简单的。设\(dis_x\)表示位置\(x\)到根的距离,则有

    \[f_x=\min\limits_{y\text{ is an ancestor of }x}f_y+p_x(dis_x-dis_y)+q_x \]

    暴力一敲,期望得分\(40\%\)。由于数据可能水了,实际得分\(60\%\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,head[1001000],cnt,a[1001000],b[1001000],dis[1001000];
    ll f[1001000];
    struct node{
    	int to,next,val;
    }edge[1001000];
    void ae(int u,int v,int w){
    	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
    }
    vector<int>v;
    void dfs(int x){
    	for(auto y:v)f[x]=min(f[x],1ll*a[x]*(dis[x]-dis[y])+b[x]+f[y]);
    	v.push_back(x);
    	for(int i=head[x];i!=-1;i=edge[i].next)dis[edge[i].to]=dis[x]+edge[i].val,dfs(edge[i].to);
    	v.pop_back();
    }
    int main(){
    	scanf("%d",&n),memset(head,-1,sizeof(head)),memset(f,0x3f3f3f3f,sizeof(f)),f[1]=0;
    	for(int i=2,x,y;i<=n;i++)scanf("%d%d%d%d",&x,&y,&a[i],&b[i]),ae(x,i,y);
    	dfs(1);
    	for(int i=2;i<=n;i++)printf("%lld\n",f[i]);
    	return 0;
    }
    

    考虑斜率优化一下。

    我们先把问题抽象到序列上。设有一个\(j<k<i\),则如果\(j\)\(k\)优,必有

    \[f_j+p_i(dis_i-dis_j)+q_i\leq f_k+p_i(dis_i-dis_k)+q_i \]

    \[f_j+p_idis_i-p_idis_j\leq f_k+p_idis_i-p_idis_k \]

    \[f_j-f_k\leq p_idis_j-p_idis_k \]

    \[f_j-f_k\leq p_i(dis_j-dis_k) \]

    \[\dfrac{f_j-f_k}{dis_j-dis_k}\geq p_i \]

    然后直接优化即可,因为\(p_i\)保证递增。

    但是,因为是在树上,所以你从一颗子树中跑出来之后,还要复原原序列!

    当然,复原是很简单的,因为斜率优化的过程中除了队首队尾的移动以外,唯一真正改动的位置就是最后的入队环节。这时候只需要记录这个位置原本放了什么即可。

    但是,暴力地维护队列的话,就不能完美地应用“单调队列”的性质了,因此复杂度最劣仍然是\(O(n^2)\)。期望得分\(60\%\)(听说常卡的好也能A?)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,head[1001000],cnt,a[1001000],b[1001000],q[1001000],l[1001000],r[1001000],cha[100100];
    ll f[1001000],dis[1001000];
    void read(int &x){
    	x=0;
    	char c=getchar();
    	while(c>'9'||c<'0')c=getchar();
    	while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
    }
    struct node{
    	int to,next,val;
    }edge[1001000];
    void ae(int u,int v,int w){
    	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
    }
    void dfs(int x){
    	while(l[x]<r[x]&&(f[q[l[x]]]-f[q[l[x]+1]])>=(dis[q[l[x]]]-dis[q[l[x]+1]])*a[x])l[x]++;
    	f[x]=(dis[x]-dis[q[l[x]]])*a[x]+b[x]+f[q[l[x]]];
    	while(l[x]<r[x]&&(f[q[r[x]-1]]-f[q[r[x]]])*(dis[q[r[x]]]-dis[x])>=(f[q[r[x]]]-f[x])*(dis[q[r[x]-1]]-dis[q[r[x]]]))r[x]--;
    	cha[x]=q[++r[x]],q[r[x]]=x;
    	printf("%d:%d,%d:",x,l[x],r[x]);for(int i=l[x];i<=r[x];i++)printf("%d ",q[i]);puts("");
    	for(int i=head[x];i!=-1;i=edge[i].next)dis[edge[i].to]=dis[x]+edge[i].val,l[edge[i].to]=l[x],r[edge[i].to]=r[x],dfs(edge[i].to);
    	q[r[x]]=cha[x];
    }
    int main(){
    	read(n),memset(head,-1,sizeof(head));
    	for(int i=2,x,y;i<=n;i++)read(x),read(y),read(a[i]),read(b[i]),ae(x,i,y);
    	l[1]=1,dfs(1);
    	for(int i=2;i<=n;i++)printf("%lld\n",f[i]);
    	return 0;
    }
    

    发现我们每个位置都那么费劲地移动左右端点太蠢了。不如直接套上一个二分。复杂度是妥妥的\(O(n\log n)\)

    (这二分真的非常难写,各种边界太恶心了)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,head[1001000],cnt,a[1001000],b[1001000],q[1001000],l[1001000],r[1001000],cha[1001000];
    ll f[1001000],dis[1001000];
    void read(int &x){
    	x=0;
    	char c=getchar();
    	while(c>'9'||c<'0')c=getchar();
    	while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
    }
    struct node{
    	int to,next,val;
    }edge[1001000];
    void ae(int u,int v,int w){
    	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
    }
    void dfs(int x){
    	int L,R,res;
    	L=l[x],R=r[x]-1,res=r[x];
    	while(L<=R){
    		int mid=(L+R)>>1;
    		if((f[q[mid]]-f[q[mid+1]])>=(dis[q[mid]]-dis[q[mid+1]])*a[x])L=mid+1;
    		else R=mid-1,res=mid;
    	} 
    	l[x]=res;
    	f[x]=(dis[x]-dis[q[l[x]]])*a[x]+b[x]+f[q[l[x]]];
    	L=l[x]+1,R=r[x],res=l[x];
    	while(L<=R){
    		int mid=(L+R)>>1;
    		if((f[q[mid-1]]-f[q[mid]])*(dis[q[mid]]-dis[x])>=(f[q[mid]]-f[x])*(dis[q[mid-1]]-dis[q[mid]]))R=mid-1;
    		else res=mid,L=mid+1;
    	}	
    	r[x]=res;
    	cha[x]=q[++r[x]],q[r[x]]=x;
    	for(int i=head[x];i!=-1;i=edge[i].next)dis[edge[i].to]=dis[x]+edge[i].val,l[edge[i].to]=l[x],r[edge[i].to]=r[x],dfs(edge[i].to);
    	q[r[x]]=cha[x];
    }
    int main(){
    	read(n),memset(head,-1,sizeof(head));
    	for(int i=2,x,y;i<=n;i++)read(x),read(y),read(a[i]),read(b[i]),ae(x,i,y);
    	l[1]=1,dfs(1);
    	for(int i=2;i<=n;i++)printf("%lld\n",f[i]);
    	return 0;
    }
    

  • 相关阅读:
    mysql 数据库引擎
    dubbo 微服务
    spring 属性文件加载接口---PropertySourceLoader
    Shiro架构
    HTTP状态码
    Shiro 修改权限,刷新权限
    Consul 架构(译)
    Java EE平台介绍(译)
    Java SPI机制
    Otb_000_ElementUI 的 Drawer组件无法上下滚动没有滚动条
  • 原文地址:https://www.cnblogs.com/Troverld/p/14598423.html
Copyright © 2020-2023  润新知