[NOIP2017模拟]做运动

2017.10.26 T2 2011

样例数据
输入

5 6
1 2 1 2
2 3 2 2
3 4 3 4
4 5 3 5
1 3 4 1
3 5 3 6
1 5

输出

3 24

分析:这道题正解我在考场上直接想出:并查集+dijkstra,具体思维过程如下:
1、“以最高温度最低为前提”?怎样才能保证走的路径是最高温度最低的呢?我想到了最小生成树(是它的思想,就是并查集判断是否连通),也就是将边按照温度从小到大排序,然后从头开始连边直到s和t连通,最后一条边的温度就是最高温度的最小值(如果后面还有温度一样的也要加进来建边,因为它们也是最高温度的最小值)。
2、图建好了,“吸收的热量尽量少”,嗯,dijkstra裸题。
但是,我在用最小生成树的思想的时候,就直接按着最小生成树模板打了!本来,这道题是只要s和t还没有连通就要把前面边都加进来,但我就写成只加能更新连通性的边,这就意味着跑最短路的图就GG了。
还好,数据虽然严格,我还是能过75%的。

代码
不要问为什么长得这么怪,因为SOJ实在是卡,每次都要TLE最后两个测试点,我想了很多方法卡常,以至于搜到了一篇神犇的博客卡常数技巧

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

inline int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;isdigit(ch);ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int maxn=500010;
const int maxm=1000010;
struct node{
    int x,y,t;
    long long c;
}bian[maxm];
int n,m,s,tt,maxt;
int fa[maxn];
int tot,first[maxn];
struct node2{
    int nxt,to;
    long long w;
}lin[maxm*2];
long long dis[maxn];
priority_queue<pair<long long,int> >que;

inline int getfa(int x)
{
    if(x==fa[x])
        return x;
    fa[x]=getfa(fa[x]);
    return fa[x];
}

inline bool comp(const node &a,const node &b)
{
    return a.t<b.t;
}

inline void addedge(int x,int y,long long z)
{
    tot++;
    lin[tot].nxt=first[x];
    first[x]=tot;
    lin[tot].to=y;
    lin[tot].w=z;
    tot++;
    lin[tot].nxt=first[y];
    first[y]=tot;
    lin[tot].to=x;
    lin[tot].w=z;
}

inline void dijkstra()
{
    for(register int i=1;i<=n;++i)
        dis[i]=5e17;
    dis[s]=0;
    que.push(make_pair(0,s));
    while(!que.empty())
    {
        int u=que.top().second;
        que.pop();
        if(u==tt)
            break;
        for(register int p=first[u];p;p=lin[p].nxt)
        {
            int v=lin[p].to;
            if(dis[v]>dis[u]+lin[p].w)
            {
                dis[v]=dis[u]+lin[p].w;
                que.push(make_pair(-dis[v],v));
            }
        }
    }
}

int main()
{
    freopen("running.in","r",stdin);
    freopen("running.out","w",stdout);

    int x,y,t2,pos;
    long long c;
    n=getint(),m=getint();
    for(register int i=1;i<=m;++i)
        bian[i].x=getint(),bian[i].y=getint(),t2=getint(),c=getint(),bian[i].t=t2,bian[i].c=t2*c;

    s=getint(),tt=getint();

    sort(bian+1,bian+m+1,comp);

    for(register int i=1;i<=n;++i)
        fa[i]=i;

    for(register int i=1;i<=m;++i)
    {
        x=bian[i].x,y=bian[i].y;
        addedge(x,y,bian[i].c);//建边是要一直建的(我放在了if(x!=y)里orz)
        x=getfa(x),y=getfa(y);
        if(x!=y)
        {
            fa[x]=y;
            x=getfa(s),y=getfa(tt);
            if(x==y)
            {
                pos=i;
                maxt=bian[i].t;
                break;
            }
        }
    }

    while(bian[++pos].t==maxt)
        addedge(bian[pos].x,bian[pos].y,bian[pos].c);

    dijkstra();

    cout<<maxt<<" "<<dis[tt]<<'\n';
    return 0;
}

本题结。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值