zoukankan      html  css  js  c++  java
  • 前往大都会

    简单的图论题。
    第一问显然答案是最短路。
    第二问中,由于有旅行路程最短的限制,旅行的过程一定在最短路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(mlog_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]);
    }
    
  • 相关阅读:
    c# vs2010 excel 上传oracle数据
    Viola-Jones人脸检測
    apache commons-configuration包读取配置文件
    linux 读取文件
    linux 统计某个文件的行数
    linux 判空处理
    linux 查看某个目录下文件的数量
    nginx 配置文件正确性测试
    使用postman上传excel文件测试导入excel
    java 反射获取字段为List类型中的泛型类型
  • 原文地址:https://www.cnblogs.com/ctmlpfs/p/14505583.html
Copyright © 2011-2022 走看看