正题
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3482
题目大意
一张有向图有正整数边权也有 x x x边权。其中 x x x可以取任何值(但是要注意所有的 x x x边必须长度相等),每次询问求 S S S到 T T T的可能最短路长度个数和它们的和。
解题思路
分层图,第 i i i层第 j j j个点表示 S S S到 i i i的最短路且经过了 j j j条 x x x的边方案,然后跑最短路。
现在我们定义 f i f_i fi表示 S S S到 E E E经过 i i i条边 x x x边的最短路,然后设 x x x边的长度为 x x x。
那么经过 i i i条边为最短路的情况当且仅当 f i + i ∗ x f_i+i*x fi+i∗x最小。那么我们可以定义若干条一次函数 y = f i + i ∗ x y=f_i+i*x y=fi+i∗x,那么就是一条以 f i f_i fi为截距, i i i为斜率的线。那么我们考虑 y = f i + i ∗ x y=f_i+i*x y=fi+i∗x为最短路的可能情况。
维护一个 y = f i + i ∗ x y=f_i+i*x y=fi+i∗x的凸包,维护一个单调队列,让剩下的线的两两之间的交点的 x x x坐标递增即可。
然后就可以算出第一问,对于第二问用等差数列计算即可。
c o d e code code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<queue>
#define ll long long
#define p(a1,a2) ((a2)*n+a1)
using namespace std;
const ll N=510,M=10100,inf=1e18;
struct node{
ll to,next,w;
}a[M*2];
struct line{
double x,y;
}l[N];
ll n,m,ls[N*N],f[N*N],tot,Q,num,ans,next[N];
double ata[N];bool v[N*N];
queue<ll> q;
void addl(ll x,ll y,ll w)
{
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;
a[tot].w=w;
}
ll read() {
ll x=0,f=1; char c=getchar();
if(c=='x') return -1;
while(!isdigit(c)) {if(c=='x')f=-f;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
return (!f)?-1:x;
}
void spfa(ll s)
{
memset(v,0,sizeof(v));
memset(f,127,sizeof(f));
q.push(s);v[s]=1;f[s]=0;
while(!q.empty())
{
ll x=q.front();q.pop();
ll c=(x-1)/n;
if(c==n) continue;
for(ll i=ls[(x-1)%n+1];i;i=a[i].next)
{
ll y=p(a[i].to,c+(a[i].w==-1));
if(f[x]+max(a[i].w,0ll)<f[y])
{
f[y]=f[x]+max(a[i].w,0ll);
if(!v[y]){
v[y]=1;
q.push(y);
}
}
}
v[x]=0;
}
return;
}
double gtan(double x1,double y1,double x2,double y2)
{return (y2-y1)/(x1-x2);}
int main()
{
n=read();m=read();
for(ll i=1;i<=m;i++)
{
ll x,y,w;
x=read();y=read();w=read();
if(x==y) continue;
addl(x,y,w);
}
Q=read();
while(Q--)
{
ll s=read(),t=read();
bool flag=0;
spfa(p(s,0));
for(ll i=0;i<=n;i++)
if(f[p(t,i)]<inf){flag=1;break;}
if(!flag) {printf("0 0\n");continue;}
if(f[p(t,0)]>inf) {printf("inf\n");continue;}
ll num=0,sum=0,cnt=0;
for(ll i=n;i>=0;i--){
if(f[p(t,i)]>inf) continue;
while(cnt>=1&>an(l[cnt].x,l[cnt].y,i,f[p(t,i)])<=ata[cnt]) cnt--;
l[++cnt]=(line){i,f[p(t,i)]};
if(cnt>1) ata[cnt]=gtan(l[cnt-1].x,l[cnt-1].y,l[cnt].x,l[cnt].y);
}
for(ll i=1;i<cnt;i++){
ll L=(int)ata[i]+1,R=(int)ata[i+1];
if(L<=R) sum+=(ll)(L*l[i].x+l[i].y+R*l[i].x+l[i].y)*(R-L+1)/2;
}
num=(ll)ata[cnt];
if(ata[cnt]!=num||cnt==1) num++,sum+=f[p(t,0)];
printf("%lld %lld\n",num,sum);
}
}