题目大意:
给定一张有向图,n<=500,m<=10000,其中有一些特殊边,长度相同是一个正整数但是未知,设为X。给出Q<=10个询问S,T,输出当x取遍所有正整数的时候,S到T的最短路的所有可能值的个数以及总和。
分析:每一条S到T的路径,设路径上x的条数有k个,其余的和为b,那么路径总长就是kx+b。对于相同的k我们只关心最小的b。于是我们可以设状态f[i][j],表示从S走到j,经过i条x的边的最小b值。最短路的边数最多只有n-1条,所以k的范围是[0,n)。状态数是O(n^2)。F[][]数组我们可以通过spfa求出(事实证明spfa比dijk快很多)。
接下来我们要处理的是n条直线y=kx+b。若无解,则肯定是n条直线的b值都无穷大;若有无穷解,则k=0的直线b值无穷大。剩下的情况一定有有限解。我们可以按k值从大到小插入平面,O(n)维护出一个凸壳,维护凸壳的时候注意存储下凸壳上相邻两条直线的整点坐标,那么相邻两条直线的对总个数的贡献就是交点横坐标差,对总和的贡献其实就是一个等差数列的和,都可以很容易算出。
综上,一次询问的复杂度就是spfa的复杂度+O(n)。
代码:
/* ID:huangta3 PROG: LANG:C++ */ #include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> using namespace std; typedef long long LL; const int maxn=503,maxm=10003,inf=1000000000; int n,m,Link[maxn],t[maxm],pre[maxm],v[maxm],S,T,f[maxn][maxn],top; bool vis[maxn][maxn]; struct Tline { int k,x; inline Tline(){} inline Tline(int _k,int _x){k=_k,x=_x;} }s[maxn]; inline int get() { int f=0,v=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-')break; if(ch=='-')f=1;else v=ch-48; while(isdigit(ch=getchar()))v=v*10+ch-48; if(f==1)return -v;else return v; } int q[maxn*maxn][2]; inline void spfa() { memset(f,120,sizeof(f)); memset(vis,0,sizeof(vis)); int front=0,rear=1; f[0][S]=0;q[front][0]=0,q[front][1]=S;vis[0][S]=1; while(front!=rear) { int K=q[front][0],T=q[front][1],V=f[K][T]; for(int i=Link[T];i;i=pre[i]) if(!v[i]) { if(K+1<n&&f[K+1][t[i]]>V) { f[K+1][t[i]]=V; if(!vis[K+1][t[i]])vis[K+1][t[i]]=1,q[rear][0]=K+1,q[rear++][1]=t[i]; } }else { if(f[K][t[i]]>V+v[i]) { f[K][t[i]]=V+v[i]; if(!vis[K][t[i]])vis[K][t[i]]=1,q[rear][0]=K,q[rear++][1]=t[i]; } } front++,vis[K][T]=0; } } inline bool spj() { int x=inf; for(int i=0;i<n;i++)x=min(x,f[i][T]); if(x>=inf)return puts("0 0")|1; else if(f[0][T]>=inf)return puts("inf")|1; return 0; } inline int calc(int k,int x){return k*x+f[k][T];} int main() { n=get();m=get(); for(int i=1;i<=m;i++) { int x=get(),y=get(),z=0; char ch=getchar(); if(ch!='x') { z=ch-'0'; while(isdigit(ch=getchar()))z=z*10+ch-'0'; } pre[i]=Link[x]; Link[x]=i; t[i]=y; v[i]=z; } int Q=get(); while(Q--) { S=get(),T=get(); spfa(); if(spj())continue; top=0; for(int i=n-1;i>=0;i--) { if(f[i][T]>=inf)continue; while(top) { int x=s[top].x,prev=calc(s[top].k,x),cur=calc(i,x); if(cur<=prev)top--;else break; } if(top) { double k1=s[top].k,k2=i,b1=f[s[top].k][T],b2=f[i][T]; int x=(int)ceil((b2-b1)/(k1-k2)); s[++top]=Tline(i,x); }else s[++top]=Tline(i,0); } int tot=1;LL sum=f[0][T]; for(int i=1;i<top;i++) { int st=s[i].x,ed=s[i+1].x-1; if(st==0)st++; tot+=ed-st+1; sum+=LL(ed-st+1)*(calc(s[i].k,st)+calc(s[i].k,ed))/2; } cout<<tot<<" "<<sum<<endl; } return 0; }