简单的图论题。
第一问显然答案是最短路。
第二问中,由于有旅行路程最短的限制,旅行的过程一定在最短路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]);
}