预处理 dis [ i ] [ j ] 表示从第 i 天到第 j 天不改变路线的最短路径
然后就可以愉快地推方程了
设 f [ i ] 表示从第一天到第 i 天的最少花费
那么 f [ j ] = min(f [ j ] , f [ i ] + dis[ i+1 ] [ j ] * (j-i) + K)
预处理的用的是 Dijkstra
注意最后改变航线的次数比我们方程里+K的次数少 1,所以最后要减 1
(我的方程初始的航线也算改变,然而初始不算改变)
// luogu-judger-enable-o2 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1007,M=27; int fir[M],from[N],to[N],val[N],cnt; inline void add(int &a,int &b,int &c) { from[++cnt]=fir[a]; fir[a]=cnt; to[cnt]=b; val[cnt]=c; } int n,m,e,d; int l[N],r[N],id[N];//存关闭港口的信息,从第l[i]到第r[i]天,id[i]号港口关闭 int dis[107][107][M]; bool p[M];//判断此时港口是否关闭 struct node { int pos,dis; inline bool operator < (const node &tmp) const { return dis>tmp.dis; } };//Dijkstra的结构体 priority_queue <node> q; void Dijk(int L,int R)//求从第L天到第R天不改变路线的最短路径 { memset(p,0,sizeof(p)); for(int i=1;i<=d;i++) { if(r[i]<L||l[i]>R) continue; p[id[i]]=1;//只要时间段有相交的区间就不能走 } q.push((node){1,0}); dis[L][R][1]=0;//愉快地跑Dijkstra while(!q.empty()) { node x=q.top(); q.pop(); if(x.dis!=dis[L][R][x.pos]) continue; for(int i=fir[x.pos];i;i=from[i]) { int &v=to[i]; if(p[v]) continue; if(dis[L][R][v]>dis[L][R][x.pos]+val[i]) { dis[L][R][v]=dis[L][R][x.pos]+val[i]; q.push((node){v,dis[L][R][v]}); } } } } int K,f[N]; int main() { memset(dis,127,sizeof(dis)); memset(f,127,sizeof(f)); f[0]=0;//注意初始化 int a,b,c; n=read(); m=read(); K=read(); e=read(); for(int i=1;i<=e;i++) { a=read(),b=read(),c=read(); add(a,b,c); add(b,a,c); } d=read(); for(int i=1;i<=d;i++) id[i]=read(),l[i]=read(),r[i]=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) Dijk(i,j);//预处理 for(int i=0;i<n;i++) for(int j=i+1;j<=n;j++) if(dis[i+1][j][m]<2e9)//注意如果太大说明无路可走就不用考虑,不然后面乘的时候可能爆成负数 f[j]=min(f[j],f[i]+dis[i+1][j][m]*(j-i)+K); printf("%d",f[n]-K);//注意最后改变次数少一 return 0; }