bzoj 3482: [COCI2013]hiperprostor 分层图最短路+凸包

题意

在遥远的未来,行星之间的食品运输将依靠单向的贸易路线。每条路径直接连接两个行星,且其运输时间是已知的。贸易商协会打算利用一项最近发现的新技术——超空间旅行,以增加一些新的航线。通过超空间旅行的航线也是单向的。由于该项技术仍处于试验阶段,超空间旅行的时间目前是未知的,但它不取决于行星之间的距离,所以每个超空间旅行的路线将花费等量的时间。下图是三个相互联通的行星及其运输时间的例子。行星使用正整数标号,超空间旅行时间记为“x”(图片对应第输入样例):过境的时间以天计,并且始终是一个正整数。贸易商协会希望对引进新航线的后果进行分析:对于某两个行星A和B,他们想知道对于任意的x,从A到B的最短路径的总中转时间的所有可能的值。

输入的第一行包含两个整数P和R,分别代表行星的数目和航线数量,1≤P≤500,0≤R≤10000。接下来的R条航线路径包含两或三个整数:行星标号C和D(1≤C,D≤P,C≠D),和T,从C到D的旅行时间。对于传统的路径,T是一个整数(1≤T≤1000000),超空间航线中,T是字符“x”。 可以存在多行有两个相同的行星。下面的行中包含的整数Q(1≤Q≤10),表示查询的数量。以下Q行包含两个整数星球标号(A和B,A≠B),为贸易商协会的查询:“从A到B的最短路径时间的可能值是什么?

输出必须包含Q行。
每一行都必须包含两个整数:不同的可能值的数目和它们的总和。如果不同的可能值的数目是无限的,该行只输出“inf”。如果不存在从A到B的路径,不同的可能值的数目及它们的总和都是0。

分析

首先跑分层图最短路,设f[i,j]表示走到点i走了j次x边的最短路。
然后对这若干条直线维护一个上凸壳,每一段用等差数列分别算贡献即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long LL;

const int N=505;

int n,m,q,cnt,last[N],stack[N],dis[N][N],s,t;
bool vis[N][10005];
struct edge{int to,next,w;}e[10005];
priority_queue<pair<int,pair<int,int> > > que;
double b[N];

int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='x'){if(ch=='-')f=-1;ch=getchar();}
    if (ch=='x') return 0;
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void addedge(int u,int v,int w)
{
    e[++cnt].to=v;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt;
}

void dij()
{
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s][0]=0;que.push(make_pair(0,make_pair(s,0)));
    while (!que.empty())
    {
        pair<int,pair<int,int> > u=que.top();que.pop();
        while (!que.empty()&&vis[u.second.first][u.second.second]) u=que.top(),que.pop();
        if (vis[u.second.first][u.second.second]) break;
        int x=u.second.first,y=u.second.second;
        vis[x][y]=1;
        for (int i=last[x];i;i=e[i].next)
            if (!e[i].w)
            {
                if (y==n) continue;
                if (dis[x][y]<dis[e[i].to][y+1])
                {
                    dis[e[i].to][y+1]=dis[x][y];
                    que.push(make_pair(-dis[e[i].to][y+1],make_pair(e[i].to,y+1)));
                }
            }
            else
            {
                if (dis[x][y]+e[i].w<dis[e[i].to][y])
                {
                    dis[e[i].to][y]=dis[x][y]+e[i].w;
                    que.push(make_pair(-dis[e[i].to][y],make_pair(e[i].to,y)));
                }
            }
    }
}

double get(int x,int y)
{
    return (double)(dis[t][y]-dis[t][x])/(x-y);
}

LL calc(int k,int b,int l,int r)
{
    return (LL)((LL)k*(l+r)+(LL)b*2)*(r-l+1)/2;
}

void solve()
{
    dij();
    int flag=0;
    for (int i=0;i<=n;i++) if (dis[t][i]!=inf) {flag=1;break;}
    if (!flag) {puts("0 0");return;}
    if (dis[t][0]==inf) {puts("inf");return;}
    int top=0;
    for (int i=n;i>=0;i--)
    {
        if (dis[t][i]==inf) continue;
        while (top&&b[top]>=get(stack[top],i)) top--;
        if (top) b[top+1]=get(stack[top],i);
        stack[++top]=i;
    }
    LL ans=0;
    for (int i=1;i<top;i++) ans+=calc(stack[i],dis[t][stack[i]],(int)b[i]+1,(int)b[i+1]);
    int num=b[top];
    if (num*stack[top-1]+dis[t][stack[top-1]]!=dis[t][0]||top==1) ans+=dis[t][0],num++;
    printf("%d %lld\n",num,ans);
}

int main()
{
    n=read();m=read();
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read(),z=read();
        addedge(x,y,z);
    }
    q=read();
    while (q--)
    {
        s=read();t=read();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值