BZOJ3482: [COCI2013]hiperprostor

13 篇文章 0 订阅
3 篇文章 0 订阅

询问不多可以当单次询问做
对于一条A到B的路径,若上面有a条特殊边,他的长度就为ax+b
先将特殊边长视为0,求出f[x][t]表示A到t,走了x条特殊边的最短路
那么A->B=a*x+f[a][B],这些路径的长度对应平面上的若干条直线,a就是这些直线的斜率
如果不存在f[0][B]就有无限种可能值,因为没有上界
否则维护一个凸包就可以求出所有最短路的可能值了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 1100;
const int maxm = 21000;

int n,m,qn,st,ed;

struct edge
{
    int xi,y,nex;
    ll c;
}a[maxm]; int len,fir[maxn];
inline void ins(const int x,const int y,const ll c,const int xi)
{
    a[++len].y=y; a[len].c=c; a[len].xi=xi;
    a[len].nex=fir[x]; fir[x]=len;
}

struct node
{
    int i,xi;
    ll x;
    node(){}
    node(const int &_x,const int &_i,const int &_xi){x=_x;i=_i;xi=_xi;}
};
inline bool operator <(node x,node y){return x.x>y.x;}

priority_queue<node>Q;
int d[maxn][maxn];
void search()
{
    for(int i=1;i<=n;i++) for(int j=0;j<=n;j++) d[i][j]=INT_MAX;

    Q.push(node(0,st,0)); d[st][0]=0;
    while(!Q.empty())
    {
        const node x=Q.top(); Q.pop(); 
        if(x.x!=d[x.i][x.xi]) continue;
        for(int k=fir[x.i];k;k=a[k].nex) 
            if(x.xi+a[k].xi<=n&&d[a[k].y][x.xi+a[k].xi]>x.x+a[k].c)
            {
                d[a[k].y][x.xi+a[k].xi]=x.x+a[k].c;
                Q.push(node(x.x+a[k].c,a[k].y,x.xi+a[k].xi));
            }
    }
}
int q[maxn],tail;
int sx[maxn];
void solve()
{
    int id=-1; 
    for(int i=0;i<=n;i++) if(d[ed][i]!=INT_MAX) {id=i; break;}
    if(id==-1) { puts("0 0"); return; }
    if(id>0) { puts("inf"); return; }

    q[tail=1]=0;
    for(int i=1;i<=n;i++) if(d[ed][i]!=INT_MAX)
    {
        if(tail&&d[ed][i]+i>d[ed][q[tail]]+q[tail]) continue;

        int ncx=1+(int)((double)(d[ed][q[tail]]-d[ed][i])/(i-q[tail]));
        while(tail>1&&ncx>=sx[tail]) 
        {
            tail--;
            ncx=1+(int)((double)(d[ed][q[tail]]-d[ed][i])/(i-q[tail]));
        }
        q[++tail]=i;
        sx[tail]=ncx;
    }
    ll ans=d[ed][0];
    int now=1,an=1;
    for(int i=tail;i>=2;i--)
    {
        if(i==2&&(sx[i]-1)*q[i]+d[ed][q[i]]==d[ed][0]) sx[i]--;
        an+=sx[i]-now;
        ans+=(ll)(sx[i]-now)*(d[ed][q[i]]+now*q[i]+d[ed][q[i]]+(sx[i]-1)*q[i])/2;
        now=sx[i];
    }
    printf("%d %lld\n",an,ans);
}

int main()
{
    char str[110];

    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        scanf("%s",str);
        if(str[0]=='x') ins(x,y,0,1);
        else
        {
            ll c=0,cl=strlen(str);
            for(int j=0;j<cl;j++) (c*=10)+=str[j]-'0';
            ins(x,y,c,0);
        }
    }
    scanf("%d",&qn);
    while(qn--)
    {
        scanf("%d%d",&st,&ed);
        search();
        solve();
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值