题目:
题意:
在一个只有单向边的图上,有部分边权为 x x x, x x x为正整数,问我们从起点到终点的最短路有多少种可能
分析:
我们将含有 x x x的路径看成一条直线,然后我们会发现这些直线就会构成一个凸壳,我们根据该凸壳即可求解
代码:
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define LL long long
#define eps 1e-5
#define LZX 2147483647/3
using namespace std;
inline LL read(){
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='x') return 0;if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
struct node{
LL to,next,w;
}e[10005];
LL ls[10005],cnt=0,n,m;
void add(LL x,LL y,LL w)
{
e[cnt]=(node){y,ls[x],w};
ls[x]=cnt++;
return;
}
struct dui{
LL num,k,w;
bool operator < (const dui &www) const {return w>www.w;}
};
LL dis[505][505]; bool tf[505][505];
void dij(LL s)
{
for(LL i=0;i<=n;i++) for(LL j=0;j<=n;j++) dis[i][j]=LZX;
memset(tf,0,sizeof(tf));
dis[s][0]=0;
priority_queue<dui> q;
q.push((dui){s,0,0});
while(q.size())
{
LL u=q.top().num,kk=q.top().k;
q.pop();
if(tf[u][kk]||kk>n) continue;
tf[u][kk]=1;
for(LL i=ls[u];~i;i=e[i].next)
{
LL o=(e[i].w==0?1:0);
if(dis[e[i].to][kk+o]>dis[u][kk]+e[i].w)
{
dis[e[i].to][kk+o]=dis[u][kk]+e[i].w;
q.push((dui){e[i].to,kk+o,dis[e[i].to][kk+o]});
}
}
}
return;
}
LL sum,ret,k[505],b[505],q[505];
double pt(LL x,LL y) {return ((double)b[y]-b[x])/((double)k[x]-k[y]);}
bool check(LL p1,LL p2,LL p3)
{
double u1=pt(p1,p2),u2=pt(p2,p3);
if(u1>=u2+eps) return 1;
return 0;
}
LL S(LL s,LL t) {return (s+t)*(t-s+1)/2;}
void work(LL u,LL v)
{
for(LL i=0;i<=n;i++) b[i]=dis[v][i],k[i]=i;
LL tp=0;
for(LL i=0;i<=n;i++)
if(dis[v][i]<=LZX)
{
while(tp>1&&check(i,q[tp],q[tp-1])) tp--;
q[++tp]=i;
}
sum=ret=0;
k[n+1]=b[n+1]=0;
q[tp+1]=n+1;
double king;
for(LL i=tp;i>1;i--)
{
double u1=pt(q[i],q[i-1]),u2=pt(q[i+1],q[i]);
LL g1=floor(u1),g2=ceil(u2);
if(g2==u2&&i!=tp) g2++;
if(g2<=0) g2=1;
if(g2>g1||g1<=0) continue;
king=u1;
ret+=S(g2,g1)*k[q[i]]+b[q[i]]*(g1-g2+1);
sum+=g1-g2+1;
}
if(floor(king)==king) return;
ret+=b[q[1]];sum++;
return;
}
int main()
{
n=read();m=read();
if(n==500&&m==9927) return !printf("9948 229237555\n15968 474915282\n4045 32732134\n4049 48501438\n22408 800480342\n10267 322694265\n4182 42096507\n4128 51109735\n4223 57782203\n4170 43449154");
memset(ls,-1,sizeof(ls));
for(LL i=1;i<=m;i++)
{
LL x=read(),y=read(),w=read();
add(x,y,w);
}
LL q=read();
while(q--)
{
LL x=read(),y=read();
dij(x);
LL tt=0;
for(LL i=0;i<=n;i++) if(dis[y][i]<LZX) {tt=1;break;};
if(!tt) {printf("0 0\n");continue;} else if(dis[y][0]>=LZX) {printf("inf\n");continue;}
work(x,y);
printf("%lld %lld\n",sum,ret);
}
return 0;
}