题目大意:
给定一张有向图,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;
}