sgu-301. Boring. Hot. Summer...

59 篇文章 1 订阅
4 篇文章 0 订阅

Description:

    给你一个 N 个点,M 条边的无向图,规定了起点 S 和终点 T ,一个人从点 S 出发去 T ,每一条 S T 的最短路叫做关键路径,属于关键路径上的点称为关键点。假设每个单位时间可以移动 1 的长度。对于第 i 个点,如果其不是关键点,输出 “ 0 ”,否则假设从 S i 时间为 Ti,输出在时间为 Ti 时,这个人可能在哪些位置上(PS:位置的定义和图的节点定义不同,不但可以站在节点上,还可以站在边上,比如一条长度为7的边,你可以站在其中长度为 1,2,3,4,5,6 的地方而不仅仅是长度为 0,7 的两端)。

   
   
   

Solution:

    首先跑一边最短路找出哪些点和哪些边在关键路径上,然后我们对于所有在关键路径上的边,假设两个端点为 u,v(dist[u]<dist[v]) ,那么我们就把 dist[u]dist[v] 的答案区间加一。
   
   
   

Code:

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;

const int Mod=131071;

int N,S,T;
int M;
int dist[2010]={0};
int st=1,en=0;
int num=0;
int hash[2010]={0};
int dui[131100]={0};
int D[2010]={0};
int Sum[2010]={0};
int Sp=0;

struct bian_
{
    int st;
    int to;
    int next;
    int dis;
}bian[400010]={{0,0,0,0}};
int First[2010]={0};
int vis[2010]={0};

void Add(int p,int q,int r,int k)
{
    bian[k].next=First[p];
    bian[k].st=p;
    bian[k].to=q;
    bian[k].dis=r;
    First[p]=k;
    return;
}

void SPFA()
{
    memset(dist,0x3f3f3f3f,sizeof(dist));
    dui[++en]=S;
    dist[S]=0;
    num=1;
    for(;num>0;)
    {
        int u=dui[st];
        st=(st&Mod)+1;
        hash[u]=0;num--;
        for(int i=First[u];i!=0;i=bian[i].next)
        {
            int v=bian[i].to;
            if(dist[v]>dist[u]+bian[i].dis)
            {
                dist[v]=dist[u]+bian[i].dis;
                if(hash[v]==0)
                {
                    hash[v]=1;
                    if(dist[v]<dist[dui[st]])
                    {
                        st=((st+Mod-1)&Mod)+1;
                        dui[st]=v;
                    }
                    else
                    {
                        en=(en&Mod)+1;
                        dui[en]=v;
                    }
                    num++;
                }
            }
        }
    }
    return;
}

int Find(int k)
{
    for(int l=1,r=Sp;l<=r;)
    {
        int mid=(l+r)>>1;
        if(D[mid]==k) return mid;
        if(D[mid]<k) l=mid+1;
        else r=mid-1;
    }
    return 0;
}

int main()
{
    scanf("%d%d%d",&N,&S,&T);
    scanf("%d",&M);
    for(int i=1;i<=M;i++)
    {
        int p,q,r;
        scanf("%d%d%d",&p,&q,&r);
        Add(p,q,r,(i<<1)-1);
        Add(q,p,r,i<<1);
    }
    SPFA();
    for(int i=1;i<=N;i++)
        D[++Sp]=dist[i];
    sort(D+1,D+N+1);
    Sp=1;
    for(int i=2;i<=N;i++)
        if(D[i]!=D[i-1])
            D[++Sp]=D[i];
    for(int i=Sp+1;i<=N;i++) D[i]=0;
    en=1;
    dui[1]=T;
    for(int i=1;i<=en;i++)
    {
        int u=dui[i];
        for(int p=First[u];p!=0;p=bian[p].next)
        {
            int v=bian[p].to;
            if(dist[u]==dist[v]+bian[p].dis)
            {
                int L=Find(dist[v]),R=Find(dist[u]);
                vis[v]=vis[u]=1;
                Sum[L+1]++,Sum[R]--;
                if(hash[v]==0)
                    dui[++en]=v,hash[v]=1;
            }
        }
    }
    for(int i=1;i<=Sp;i++)
        Sum[i]+=Sum[i-1];
    for(int i=1;i<=N;i++)
        if(vis[i]==1)
            Sum[Find(dist[i])]++;
    for(int i=1;i<=N;i++)
    {
        int p=Find(dist[i]);
        printf("%d ",Sum[p]*vis[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值