前往大都会

简单的图论题。
第一问显然答案是最短路。
第二问中,由于有旅行路程最短的限制,旅行的过程一定在最短路dag上。
建立最短路dag。(dag的条件非常重要)
每条铁路会被不在最短路dag上的所有边分割成若干个片段。
考虑dp,设\(f_i\)表示从源点到达\(i\)的最小代价。
可以枚举\(i\)所在的所有铁路线上的点\(j\)进行转移。
把被分割的铁路线进行前缀和,设为\(s\)
\(f_i=\min(f_j+(s_i-s_j)^2)\)
直接做时间复杂度是\(O(n^2)\)的。
73分代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 1000010
int n,m,h[N],w[N*2],v[N*2],nxt[N*2],ec,d[N],vi[N],tp,st[N],du[N],ct,cc,cv,po[N],ii[N];
struct nn{
	int x,v;
};
struct hl{
	
};
int operator <(nn x,nn y){
	return x.v>y.v;
}
priority_queue<nn>q;
queue<int>tq;
vector<int>s[N],t[N],g[N],vt[N],p[N],id[N];
void dij(){
	q.push((nn){1,0});
	for(int i=2;i<=n;i++)
		d[i]=1e18;
	while(!q.empty()){
		nn x=q.top();
		q.pop();
		if(vi[x.x])
			continue;
		vi[x.x]=1;
		for(int i=h[x.x];i;i=nxt[i])
			if(d[v[i]]>d[x.x]+w[i]){
				d[v[i]]=d[x.x]+w[i];
				q.push((nn){v[i],d[v[i]]});
			}
	}
}
void tt(){
	for(int i=1;i<=n;i++)
		if(!du[i])
			tq.push(i);
	while(!tq.empty()){
		int x=tq.front();
		tq.pop();
		st[++tp]=x;
		ii[x]=tp;
		for(int i=h[x];i;i=nxt[i]){
			du[v[i]]--;
			if(!du[v[i]])
				tq.push(v[i]);
		}
	}
}
void dd(){
	for(int i=1;i<=n;i++)
		d[i]=0;
	for(int i=1;i<=n;i++){
		int x=st[i],c=0;
		for(int j=0;j<t[x].size();j++){
			int y=t[x][j];
			for(int k=0;k<p[y].size();k++){
				int z=p[y][k],i1=id[x][c],va=s[y][i1]-s[y][k];
				if(ii[z]>ii[x])
					d[z]=max(d[z],d[x]+va*va);
			}
			c++;
		}
	}
}
void add(int x,int y,int z){
	v[++ec]=y;
	w[ec]=z;
	nxt[ec]=h[x];
	h[x]=ec;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++){
		int s,a,b;
		scanf("%lld",&s);
		for(int j=1;j<=s;j++){
			scanf("%lld%lld",&a,&b);
			g[i].push_back(a);
			vt[i].push_back(b);
		}
		scanf("%lld",&a);
		g[i].push_back(a);
	}
	for(int i=1;i<=m;i++)
		for(int j=0;j+1<g[i].size();j++)
			add(g[i][j],g[i][j+1],vt[i][j]);
	dij();
	for(int i=1;i<=m;i++){
		int la=0;
		for(int j=0;j+1<g[i].size();j++){
			if(d[g[i][j+1]]!=d[g[i][j]]+vt[i][j]){
				cc++;
				for(int k=la;k<=j;k++){
					p[cc].push_back(g[i][k]);
				}
				for(int k=la;k<=j;k++){
					t[g[i][k]].push_back(cc);
					id[g[i][k]].push_back(k-la);
				}
				s[cc].resize(p[cc].size());
				for(int k=0;k+1<s[cc].size();k++)
					s[cc][k+1]=s[cc][k]+vt[i][k+la];
				la=j+1;
			}
		}
		cc++;
		for(int k=la;k<g[i].size();k++)
			p[cc].push_back(g[i][k]);
		for(int k=la;k<g[i].size();k++){
			t[g[i][k]].push_back(cc);
			id[g[i][k]].push_back(k-la);
		}
		s[cc].resize(p[cc].size());
		for(int k=0;k+1<s[cc].size();k++)
			s[cc][k+1]=s[cc][k]+vt[i][k+la];
	}
	printf("%lld ",d[n]);
	memset(h,0,sizeof(h));
	ec=0;
	for(int i=1;i<=m;i++)
		for(int j=0;j+1<g[i].size();j++)
			if(d[g[i][j+1]]==d[g[i][j]]+vt[i][j]){
				add(g[i][j],g[i][j+1],0);
				du[g[i][j+1]]++;
			}
	tt();
	dd();
	printf("%lld ",d[n]);
}

但是发现\(f_j+(s_i-s_j)^2=f_j+s_i^2-2*s_i*s_j+s_j^2=(s_i^2)+(-2s_i)s_j+(f_j+s_j^2)\)
后面是直线方程,显然可以使用凸包维护。
维护\(m\)个凸包,按照拓扑序枚举当前点。
枚举当前点所在铁路(设为数组\(a\)),在\(a\)对应的凸包上查询得到当前点的\(f\)
然后把查询得到结果插入\(a\)对应的凸包中。
由于\(s\)是单调递增的,所以可以线性维护凸包。
时间复杂度\(O(m\log_2n)\),瓶颈在最短路。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 1000010
int n,m,h[N],w[N*2],v[N*2],nxt[N*2],ec,d[N],vi[N],tp,st[N],du[N],ct,cc,cv,po[N],ii[N];
struct nn{
	int x,v;
};
struct no{
	int x,y;
}a[N];
int va(no x,int y){
	return x.x*y+x.y;
}
int cp(no j,no k,no i){
	return (k.y-j.y)*(i.x-k.x)<(i.y-k.y)*(k.x-j.x);
}
struct hl{
	vector<no>s;
	void ps(no x){
		if(s.size()<2){
			s.push_back(x);
			return;
		}
		while(s.size()>1&&cp(s[s.size()-2],s.back(),x))
			s.pop_back();
		s.push_back(x);
	}
	int qu(int x){
		if(!s.size())
			return 0;
		while(s.size()>1&&(s.back().y-s[s.size()-2].y)<=2*x*(s.back().x-s[s.size()-2].x)){
			tp--;
			s.pop_back();
		}
		return -2*s.back().x*x+s.back().y+x*x;
	}
}bl[N];
int operator <(nn x,nn y){
	return x.v>y.v;
}
priority_queue<nn>q;
queue<int>tq;
vector<int>s[N],t[N],g[N],vt[N],p[N],id[N];
void dij(){
	q.push((nn){1,0});
	for(int i=2;i<=n;i++)
		d[i]=1e18;
	while(!q.empty()){
		nn x=q.top();
		q.pop();
		if(vi[x.x])
			continue;
		vi[x.x]=1;
		for(int i=h[x.x];i;i=nxt[i])
			if(d[v[i]]>d[x.x]+w[i]){
				d[v[i]]=d[x.x]+w[i];
				q.push((nn){v[i],d[v[i]]});
			}
	}
}
void tt(){
	for(int i=1;i<=n;i++)
		if(!du[i])
			tq.push(i);
	while(!tq.empty()){
		int x=tq.front();
		tq.pop();
		st[++tp]=x;
		ii[x]=tp;
		for(int i=h[x];i;i=nxt[i]){
			du[v[i]]--;
			if(!du[v[i]])
				tq.push(v[i]);
		}
	}
}
void dd(){
	for(int i=1;i<=n;i++)
		d[i]=0;
	for(int i=1;i<=n;i++){
		int x=st[i],c=0;
		for(int j=0;j<t[x].size();j++){
			int y=t[x][j],i1=id[x][c],sv=s[y][i1];
			d[x]=max(d[x],bl[y].qu(sv));
			c++;
		}
		c=0;
		for(int j=0;j<t[x].size();j++){
			int y=t[x][j],i1=id[x][c],sv=s[y][i1];
			bl[y].ps((no){sv,sv*sv+d[x]});
			c++;
		}
	}
}
void add(int x,int y,int z){
	v[++ec]=y;
	w[ec]=z;
	nxt[ec]=h[x];
	h[x]=ec;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++){
		int s,a,b;
		scanf("%lld",&s);
		for(int j=1;j<=s;j++){
			scanf("%lld%lld",&a,&b);
			g[i].push_back(a);
			vt[i].push_back(b);
		}
		scanf("%lld",&a);
		g[i].push_back(a);
	}
	for(int i=1;i<=m;i++)
		for(int j=0;j+1<g[i].size();j++)
			add(g[i][j],g[i][j+1],vt[i][j]);
	dij();
	for(int i=1;i<=m;i++){
		int la=0;
		for(int j=0;j+1<g[i].size();j++){
			if(d[g[i][j+1]]!=d[g[i][j]]+vt[i][j]){
				cc++;
				for(int k=la;k<=j;k++){
					p[cc].push_back(g[i][k]);
				}
				for(int k=la;k<=j;k++){
					t[g[i][k]].push_back(cc);
					id[g[i][k]].push_back(k-la);
				}
				s[cc].resize(p[cc].size());
				for(int k=0;k+1<s[cc].size();k++)
					s[cc][k+1]=s[cc][k]+vt[i][k+la];
				la=j+1;
			}
		}
		cc++;
		for(int k=la;k<g[i].size();k++)
			p[cc].push_back(g[i][k]);
		for(int k=la;k<g[i].size();k++){
			t[g[i][k]].push_back(cc);
			id[g[i][k]].push_back(k-la);
		}
		s[cc].resize(p[cc].size());
		for(int k=0;k+1<s[cc].size();k++)
			s[cc][k+1]=s[cc][k]+vt[i][k+la];
	}
	printf("%lld ",d[n]);
	memset(h,0,sizeof(h));
	ec=0;
	for(int i=1;i<=m;i++)
		for(int j=0;j+1<g[i].size();j++)
			if(d[g[i][j+1]]==d[g[i][j]]+vt[i][j]){
				add(g[i][j],g[i][j+1],0);
				du[g[i][j+1]]++;
			}
	tt();
	dd();
	printf("%lld ",d[n]);
}
posted @ 2021-03-09 15:00  celerity1  阅读(64)  评论(0)    收藏  举报