Meeting HDU - 5521

题目:

一天zjx想和她的长老一起去百盛看电影。然而zjx在21b573而长老在天美,因此他们想先集合然后一起出发去百盛。因为他们很恩爱所以他们想尽快和彼此会和。现给出一张有n个点的图,(zjx和长老可以在这n个任意一个点中会和),规定1为21b573,n为天美。现在有m种传送阵,分布在各个点上,每个相同的传送阵可以相互传送,但需要花费ti分钟。
问zjx和长老需要花费多长时间才能见到面。

Input

第一行输入一个整数T(1<=T<=6),表示样例数。紧接着T行
第一行输入两个整数n,m(2<=n<=10^5).之后紧接着m行代表每种传送阵
每行包括两个整数ti(1<=ti<=10^9),si。ti表示在这种传送阵中传送需要消耗ti时间,
si表示这种传送阵分布在si个地方,紧跟着输入si个数来表示该传送阵所处的点编号。
保证si的和小于等于10^6.

Output

对于每一组样例,如果他们不能会和,则输出一行"Evil John"(没有引号)
如果能,则输出两行,第一行一个整数代表他们需要花费的时间
第二行代表他们可能相聚的地点。如果有多种可能,以一个递增序列将他们全输出出来

样例

Input

5 4
1 3 1 2 3
2 2 3 4
10 2 1 5
3 3 3 4 5
3 1
1 2 1 2

Output

Case #1: 3
3 4
Case #2: Evil John

Hint

In the first case, it will take Bessie 1 minute travelling to the 3rd block, and it will take Elsie 3 minutes travelling to the 3rd block. It will take Bessie 3 minutes travelling to the 4th block, and it will take Elsie 3 minutes travelling to the 4th block. In the second case, it is impossible for them to meet.

思路

建图

对于一些建图的问题,是集合式的,比如给ai个点,并且说这ai个点互相到达的距离为w,但是ai^2的时间建图是不可能的的,可以开一个中间节点建图,一种是 将所有ai连一条单向边到s ,边权为0,再由s向每个ai连一条单向边 ,边权为w。还有一种是所有节点都向s连一条无向边,边权为w/2;其实只要是连向中间节点的边权和连出来的的边权之和等于w即可。

针对这道题

从起点跑一遍最短路,再从终点跑一遍最短路,然后到每个节点的相遇最小时间即为ans[i]=max(dis[s][i],dis[t][i]);

代码

#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define maxm 1000005
#define maxn 200005
#define INF 0x3f3f3f3f
#define ll long long
//#define mp(x,y) make_pair(x,y)
int t,n,m;
struct node{
    int v,w,nxt;
    node(int v,int w,int nxt):v(v),w(w),nxt(nxt){};
    node(){};
};
node ed[maxm*2];
int head[maxn],tot;
bool vis[maxn];
void addedge(int u,int v,int w)
{
    ed[++tot].v=v;ed[tot].w=w;ed[tot].nxt=head[u];head[u]=tot;
}
ll dis[maxn][2];
queue<int>q;
vector<int>v;
int main()
{
    scanf("%d",&t);
    int kase=0;
    while(t--){
        v.clear();
        tot=0;
        memset(head,0,sizeof(head));
        memset(vis,0,sizeof(vis));
        scanf("%d%d",&n,&m);
        for(int i=1,x,y,z;i<=m;i++){
            scanf("%d%d",&x,&y);
            for(int j=1;j<=y;j++){
                scanf("%d",&z);
                addedge(n+i,z,x);
                addedge(z,n+i,0);
                //addedge(n+1,z,x);
                //addedge(z,n+1,x);
            }
        }
        for(int i=0;i<maxn;i++){
            dis[i][0]=dis[i][1]=1e18;
        }
        dis[1][0]=0;
        q.push(1);
        vis[1]=1;
        while(!q.empty()){
            int u=q.front();q.pop();vis[u]=0;
            for(int i=head[u];i;i=ed[i].nxt){
                node now=ed[i];
                int v=now.v;
                int w=now.w;
                if(dis[v][0]>dis[u][0]+1ll*w){
                    dis[v][0]=dis[u][0]+1ll*w;
                    if(vis[v]==0){
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        memset(vis,0,sizeof(vis));
        dis[n][1]=0;
        q.push(n);
        vis[n]=1;
        while(!q.empty()){
            int u=q.front();q.pop();vis[u]=0;
            for(int i=head[u];i;i=ed[i].nxt){
                node now=ed[i];
                int v=now.v;
                int w=now.w;
                if(dis[v][1]>dis[u][1]+1ll*w){
                    dis[v][1]=dis[u][1]+1ll*w;
                    if(vis[v]==0){
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        ll ans=1e18;
        for(int i=1;i<=n;i++){
            if(ans>max(dis[i][0],dis[i][1])){
                ans=max(dis[i][0],dis[i][1]);
            }
            //printf("%d ",dis[i][0]);
        }
        //printf("\n");
        for(int i=1;i<=n;i++){
            //printf("%d ",dis[i][1]);
            if(ans==max(dis[i][0],dis[i][1]))v.push_back(i);
        }
        //printf("\n");
        if(ans==1e18){
            printf("Case #%d: Evil John\n",++kase);
            continue;
        }
        printf("Case #%d: %lld\n",++kase,ans);
        int k=v.size()-1;
        for(int i=0;i<=k;i++){
            printf("%d%c",v[i],i==k?'\n':' ');
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值