SGU103代码能力训练

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;

#define int long long

const int N = 405;
const int M = 28005 * 2;

int gcd(int x , int y) {
    return (y == 0) ? x : gcd(y , x  % y);
}

int lcm(int x , int y) {
    return x * y / gcd(x , y);
}

struct Mod_holder {
    int mod , l , r , Min;
    void check(void) {
        printf("%lld %lld %lld %lld\n" , mod , l , r , Min);
    }
}col[N][2] , Ind , Tmp;

// mod holder

int fir[N] , ne[M] , to[M] , C[M] , cnt;

#define Foreachson(i , x) for(int i = fir[x];i;i = ne[i])

void add(int x , int y , int w) {
    ne[++ cnt] = fir[x]; fir[x] = cnt; to[cnt] = y; C[cnt] = w;
}

void link(int x , int  y , int w) {
    add(x , y , w); add(y , x , w);
}

// Edge dancer

bool in (int now , Mod_holder xxx) {
    now %= xxx.mod;
    if(xxx.Min < 0) {
        xxx.l += xxx.mod; xxx.r += xxx.mod;
        if(now >= xxx.l && now <= xxx.r) return 1;
        xxx.l -= xxx.mod; xxx.r -= xxx.mod;
    }
    // Åжϱ߽çҪעÒâ. 
    if(now >= xxx.l && now <= xxx.r) return 1;
    return 0;
}

int Min_Time_get (Mod_holder a , Mod_holder b , int from , int time) {
    from = max(from , max(a.Min , b.Min));
    int all = from;
    while(!in(from , a) || !in(from , b)) {
        from ++;
        if(from > lcm(a.mod , b.mod) * 2 + all) return -1;
    }
    return from;
}

// shortest path processor

struct Point {
    int cur , time , pre;
    friend bool operator < (Point xxx , Point yyy) {
        return xxx.time > yyy.time;
    }
}ind , tmp;

priority_queue <Point> q;

int s , t , dis[N] , pre[N];
bool vis[N];

void push(int cur , int time , int pre) {
    tmp.cur = cur; tmp.time = time; tmp.pre = pre;
    q.push(tmp);
}

void print(int x) {
    if(x != s) print(pre[x]);
    if(x == t) printf("%lld" , x);
    if(x == t) return;
    printf("%lld " , x);
}

void dijkstra(int s) {
    while(!q.empty()) q.pop();
    push(s , 0 , 0);
    while(!q.empty()) {
        while(!q.empty() && vis[q.top().cur]) q.pop();
        if(q.empty()) break;
        ind = q.top(); q.pop();
        dis[ind.cur] = ind.time; pre[ind.cur] = ind.pre;vis[ind.cur] = 1;
        Foreachson(i , ind.cur) {
            int V = to[i];
            if(vis[V]) continue;
            int Min = 2e9;
            for(int j = 0;j <= 1;j ++) {
                Ind = col[ind.cur][j]; Tmp = col[V][j];
                int now = Min_Time_get(Ind , Tmp , ind.time , 0);
                if(now == -1) continue;
                Min = min(Min , now);
            }
            Min += C[i];
            if(Min > 2e8) continue;
            push(V , Min , ind.cur);
        }
    }
    if(vis[t]) {
        printf("%lld\n" , dis[t]); print(t);
    }
    else puts("0");
}

int n ,  m;

void build(void) {
    scanf("%lld%lld" , &s , &t);
    scanf("%lld%lld" , &n , &m);
    for(int i = 1;i <= n;i ++) {
        char c = getchar(); int still , B , P;
        while(c != 'P' && c != 'B') c = getchar();
        scanf("%lld%lld%lld" , &still , &B , &P);
        int ty = 0; if(c == 'P') ty = 1;
        col[i][0].mod = col[i][1].mod = B + P;
        if(ty == 0) {
            col[i][0].Min = still - B + 1; col[i][0].l = still - B + 1; col[i][0].r = still;
            col[i][1].Min = still + 1; col[i][1].l = still + 1; col[i][1].r = still + P; 
        }
        else {
            col[i][0].Min = still - P + 1; col[i][0].l = still - P + 1; col[i][0].r = still;
            col[i][1].Min = still + 1; col[i][1].l = still + 1; col[i][1].r = still + B;
            swap(col[i][0] , col[i][1]);
        }
        col[i][0].l --; col[i][0].r --; col[i][1].l --; col[i][1].r --; col[i][0].Min --; col[i][1].Min --;
    }
    for(int i = 1;i <= m;i ++) {
        int x , y , w;
        scanf("%lld%lld%lld" , &x , &y , &w);
        link(x , y , w);
    }
}

main(void) {
//  freopen("data.txt" , "r" , stdin);
//  freopen("myout.txt" , "w" , stdout);
    build();
    dijkstra(s);
    return 0;
}

这题对问题边界的考虑要十分清晰, 难点在于找时间的下一个相遇点,这里我原本用了十分繁琐的方法,其实完全没有必要,直接狂扫就可以了,并且退出时的边界导致我调试了很久,这种地方一定要在打代码前注意,多分函数,要加强读题和信息转化能力.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值