【集训】最短路!

最短路

P4779 【模板】单源最短路径(标准版)

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    const int N = 200005;
    int n, m, s;
    int h[N], e[N << 1], ne[N << 1], w[N << 1], idx;
    vector<PII> v[N];
    int dis[N];
    struct node{
        int d, id;
        bool friend operator<(node a,node b){
            return a.d > b.d;
        }
    };
    void add(int u,int v,int W){
        e[++idx] = v, ne[idx] = h[u], w[idx] = W, h[u] = idx;
    }
    priority_queue<node> q;
    void dij(){
        memset(dis, 0x3f, sizeof(dis));
        dis[s] = 0;
        q.push({0, s});
        while(!q.empty()){
            int d = q.top().d, u = q.top().id;
            q.pop();
            if(d>dis[u])
                continue;
            for (int i = h[u]; i;i=ne[i]){
                int v = e[i];
                if(dis[v]>dis[u]+w[i]){
                    dis[v] = dis[u] + w[i];
                    q.push({dis[v], v});
                }
            }
        }
    }
    
    void Main(){
        cin >> n >> m >> s;
        for (int i = 1; i <= m;i++){
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, w);
        }
        dij();
        for (int i = 1; i <= n;i++)
            cout << dis[i] << " ";
    }

}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    // T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}

P5905 【模板】全源最短路(Johnson)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << '=' << x << endl

inline int rd() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

void print(ll x) {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
    return;
}

namespace Star_F {

struct edge {
    int v, w, next;
} e[10005];

struct node {
    int dis, id;
    bool operator<(const node& a) const { return dis > a.dis; }
    node(int d, int x) { dis = d, id = x; }
};

const int INF = 1e9;
int head[5005], vis[5005], t[5005];
int cnt, n, m;
long long h[5005], dis[5005];

void addedge(int u, int v, int w) {
    e[++cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt;
}

bool spfa(int s) {
    queue<int> q;
    memset(h, 63, sizeof(h));
    h[s] = 0, vis[s] = 1;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].v;
            if (h[v] > h[u] + e[i].w) {
                h[v] = h[u] + e[i].w;
                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                    t[v]++;
                    if (t[v] == n + 1) return false;
                }
            }
        }
    }
    return true;
}

void dijkstra(int s) {
    priority_queue<node> q;
    for (int i = 1; i <= n; i++) dis[i] = INF;
    memset(vis, 0, sizeof(vis));
    dis[s] = 0;
    q.push(node(0, s));
    while (!q.empty()) {
        int u = q.top().id;
        q.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].v;
            if (dis[v] > dis[u] + e[i].w) {
                dis[v] = dis[u] + e[i].w;
                if (!vis[v]) q.push(node(dis[v], v));
            }
        }
    }
    return;
}

void Main() {
    n = rd(); m = rd();
    for (int i = 1; i <= m; i++) {
        int u = rd(), v = rd(), w = rd();
        addedge(u, v, w);
    }
    for (int i = 1; i <= n; i++) addedge(0, i, 0);
    if (!spfa(0)) {
        cout << -1 << endl;
        return;
    }
    for (int u = 1; u <= n; u++)
        for (int i = head[u]; i; i = e[i].next) e[i].w += h[u] - h[e[i].v];
    for (int i = 1; i <= n; i++) {
        dijkstra(i);
        long long ans = 0;
        for (int j = 1; j <= n; j++) {
            if (dis[j] == INF)
                ans += j * INF;
            else
                ans += j * (dis[j] + h[j] - h[i]);
        }
        cout << ans << endl;
    }
}

}

signed main() {
    // freopen(".in", "r", stdin);
    // freopen(".out", "w", stdout);
    return Star_F::Main(), 0;
}

P3385 【模板】负环

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    const int N = 2005;
    int dis[N], cnt[N];
    vector<PII> G[N];
    bool vis[N];
    int n, m;
    bool spfa(){
        queue<int> q;
        vis[1] = 1, dis[1] = 0, cnt[1] = 1;
        q.push(1);
        while(!q.empty()){
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for (int i = 0; i < G[u].size();i++){
                int v = G[u][i].fi, w = G[u][i].se;
                if(dis[v]>dis[u]+w){
                    dis[v] = dis[u] + w;
                    if(!vis[v]){
                        cnt[v]++;
                        q.push(v);
                        if(cnt[v]>=n)
                            return true;
                    }
                }
            }
        }
        return false;
    }
    void Main(){
        memset(dis, 0x3f, sizeof(dis));
        memset(vis, 0, sizeof(vis));
        memset(cnt, 0, sizeof(cnt));
        cin >> n >> m;
        for (int i = 1; i <= n;i++)
            G[i].clear();
        for (int i = 1; i <= m;i++){
            int u, v, w;
            cin >> u >> v >> w;
            G[u].push_back({v, w});
            if(w>=0)
                G[v].push_back({u, w});
        }
        cout << (spfa() ? "YES" : "NO") << endl;
    }

}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}

P1119 灾后重建

动态加边,动态求全源最短路。

由于输入的时间 t 按升序排序,不用再排序。直接暴力加边,用 Floyd 维护全源最短路即可。

注意编号是 0N1 ,所以数组从 0 开始。

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    const int N = 205;
    int T, n, m, a[N], f[N][N];

    void update(int k){
        for (int i = 0; i < n;i++){
            for (int j = 0; j < n;j++)
                f[i][j] = f[j][i] = min(f[i][j], f[i][k] + f[k][j]);
        }

    }
    void Main(){
        cin >> n >> m;
        for (int i = 0; i < n;i++)
            cin >> a[i];
        memset(f, 0x3f, sizeof(f));
        for (int i = 0; i <= n;i++)
            f[i][i] = 0;
        for (int i = 1; i <= m;i++){
            int u, v, w;
            cin >> u >> v >> w;
            f[u][v] = f[v][u] = w;
        }
        cin >> T;
        int x = 0;
        while(T--){
            int u, v, t;
            cin >> u >> v >> t;
            while(a[x]<=t&&x<n)
                update(x++);
            if(a[u]>t||a[v]>t)
                cout << -1 << endl;
            else{
                if(f[u][v]==0x3f3f3f3f)
                    cout << -1 << endl;
                else
                    cout << f[u][v] << endl;
            }
        }
    }

}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    // T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}

P1462 通往奥格瑞玛的道路

看到最大值最小,果断考虑二分。

我们二分一个 mid,每次只经过边权小于 mid 的边,判断是否能到达终点。

如何判断?和最短路类似,我们用 dis[v]=dis[u]-w[i] 更新 dis 数组即可,所以用大根堆维护。

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    const int N = 10005, M = 100005, INF = 0x3f3f3f3f;
    int h[N], ne[M], e[M], w[M], idx;
    int n, m, k;
    int a[N], l = INF, r, flag;
    void add(int u,int v,int W){
        e[++idx] = v, ne[idx] = h[u], w[idx] = W, h[u] = idx;
    }
    int dis[N];
    priority_queue<PII> q;
    bool check(int mid){
        memset(dis, -1, sizeof(dis));
        dis[1] = k;
        q.push({dis[1], 1});
        while(!q.empty()){
            int u = q.top().se, d = q.top().fi;
            q.pop();
            if(dis[u]!=d)
                continue;
            for (int i = h[u]; i;i=ne[i]){
                int v = e[i];
                if(a[v]>mid)
                    continue;
                if(dis[v]<dis[u]-w[i]&&dis[u]-w[i]>=0){
                    dis[v] = dis[u] - w[i];
                    q.push({dis[v], v});
                }
            }
        }
        return dis[n] != -1;
    }
    void Main(){
        cin >> n >> m >> k;
        for (int i = 1; i <= n;i++){
            cin >> a[i];
            l = min(l, a[i]), r = max(r, a[i]);
        }
        flag = r;
        for (int i = 1; i <= m;i++){
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, w), add(v, u, w);
        }
        l = a[1];
        while(l<=r){
            int mid = l + r >> 1;
            if(check(mid))
                r = mid - 1;
            else
                l = mid + 1;
        }
        if(l==flag+1)
            cout << "AFK" << endl;
        else
            cout << l << endl;
    }

}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    // T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}

UVA11090 Going in Cycle!!

经典套路二分题

二分答案 w,原式 ciw×cnt。化简得 cnt(ciw)0。转换为找负环。

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    const int INF = 0x3f3f3f3f;
    const int N = 305, M = 15000;
    int m, n;
    int h[N], e[M], ne[M], idx;
    double w[M];
    int tmp = 1;
    void add(int u,int v,double W){
        e[++idx] = v, ne[idx] = h[u], w[idx] = W, h[u] = idx;
    }
    int nm[N], vis[N];
    double dis[N];
    bool spfa(double x){
        queue<int> q;
        memset(vis, 1, sizeof(vis));
        for (int i = 1; i <= n;i++)
            dis[i] = (double)INF;
        memset(nm, 0, sizeof(nm));
        for (int i = 1; i <= m;i++)
            w[i] -= x;
        for (int i = 1; i <= n;i++)
            q.push(i);
        
        while(!q.empty()){
            int now = q.front();
            q.pop();
            vis[now] = 0;
            for (int i = h[now]; i;i=ne[i]){
                int v=e[i];
                if(dis[v]>=dis[now]+w[i]){
                    dis[v] = dis[now] + w[i];
                    nm[v] = nm[now] + 1;
                    if(vis[v]==0){
                        q.push(v);
                    }
                    if(nm[v]>=n+1){
                        for (int i = 1; i <= m;i++)
                            w[i] += x;
                        return 1;
                    }
                }
            }
        }
        for (int i = 1; i <= m;i++) w[i]+=x;
        return 0;
    }
    void Main(){
        idx = 0;
        memset(h, 0, sizeof(h));
        cin >> n >> m;
        for (int i = 1; i <= m;i++){
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, (double)w);
        }
        double l = 0, r = 100000001;
        while(r-l>0.0000001){
            double mid = (l + r) / 2;
            
            if(spfa(mid))
                r = mid;
            else l = mid;
        }
        cout << "Case #" << tmp++ << ": ";
        if(r==100000001)
            cout << "No cycle found." << endl;
        else
            printf("%.2lf\n", l);
    }

}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}

P2865 [USACO06NOV] Roadblocks G

单源次短路模板题目

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    const int N=200005;
    int n, m;
    int h[N], e[N], ne[N], w[N], idx;
    int dis[2][N];
    void add(int u,int v,int W){
        e[++idx] = v, ne[idx] = h[u], w[idx] = W, h[u] = idx;
    }
    struct node{
        int id, d;
        bool friend operator<(node a,node b){
            return a.d > b.d;
        }
    };
    priority_queue<node> q;
    void dij(){
        for (int i = 1; i <= n;i++)
            dis[0][i] = dis[1][i] = 2147483647;
        dis[0][1] = 0;
        q.push({1, 0});
        while(!q.empty()){
            int u = q.top().id, d = q.top().d;
            q.pop();
            if(d>dis[1][u])
                continue;
            for (int i = h[u]; i;i=ne[i]){
                int v = e[i];
                if(dis[0][v]>d+w[i]){
                    dis[1][v] = dis[0][v];
                    dis[0][v] = d + w[i];
                    q.push({v, dis[0][v]});
                }
                if(dis[1][v]>d+w[i]&&dis[0][v]<d+w[i]){
                    dis[1][v] = d + w[i];
                    q.push({v, dis[1][v]});
                }
            }
        }
    }
    void Main(){
        cin >> n >> m;
        for (int i = 1; i <= m;i++){
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, w), add(v, u, w);
        }
        dij();
        cout << dis[1][n] << endl;
    }

}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    // T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}

P1875 佳佳的魔法药水

最短路计数问题。

#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
#define rd rd()

void wt(int x){
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        wt(x / 10);
    putchar(x % 10 + '0');
    return;
}
void wt(char x){
    putchar(x);
}
void wt(int x, char k){
    wt(x),putchar(k);
}

namespace Star_F{
    const int N = 3005;
    int c[N], ans[N], t[N][N];
    bool f[N];
    void Main(){
        int n;
        cin >> n;
        for (int i = 1; i <= n;i++)
            cin >> c[i], ans[i] = 1;
        int u, v, w;
        while(scanf("%d%d%d",&u,&v,&w)!=EOF)
            t[u + 1][v + 1] = t[v + 1][u + 1] = w + 1;

        for (int i = 1; i < n;i++){
            int maxn = 0x3f3f3f3f;
            int b;
            for (int j = 1; j <= n;j++)
                if(!f[j]&&c[j]<maxn)
                    b = j, maxn = c[j];
            f[b] = 1;
            for (int j = 1; j <= n;j++)
                if(f[j]&&t[b][j]){
                    if(c[b]+c[j]==c[t[b][j]])
                        ans[t[b][j]] += ans[b] * ans[j];
                    if(c[b]+c[j]<c[t[b][j]])
                        c[t[b][j]] = c[b] + c[j], ans[t[b][j]] = ans[b] * ans[j];
                }
        }
        cout << c[1] << " " << ans[1] << endl;
    }

}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ClockA;
    int T=1;
    // T=rd;
    while(T--) Star_F::Main();
    // ClockB;
    return 0;
}

P1948 [USACO08JAN] Telephone Lines S

最大值最小,还是考虑二分。

二分 mid ,把大于 mid 的边看为 1,小于等于 mid 得便看为 0,跑最短路即可

当然更优秀的做法是 01BFS

#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 20010;
int n, m, k;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
deque<int> q;
bool st[N];
void add(int a, int b, int c){
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
bool check(int bound){
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);
    q.push_back(1);
    dist[1] = 0;
    while (q.size()){
        int t = q.front();
        q.pop_front();

        if (st[t]) continue;
        st[t] = true;

        for (int i = h[t]; ~i; i = ne[i]){
            int j = e[i], x = w[i] > bound;
            if (dist[j] > dist[t] + x){
                dist[j] = dist[t] + x;
                if (!x) q.push_front(j);
                else q.push_back(j);
            }
        }
    }
    return dist[n] <= k;
}
int main(){
    cin >> n >> m >> k;
    memset(h, -1, sizeof h);
    while (m -- ){
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }
    int l = 0, r = 1e6 + 1;
    while (l < r){
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    if (r == 1e6 + 1) cout << -1 << endl;
    else cout << r << endl;
    return 0;
}

P2371 [国家集训队] 墨墨的等式

奇妙好题!!

同余最短路:与差分约束有异曲同工之妙,都将约束条件转化为边,每种状态转化为点。把本来与图论毫不相干的问题抽象到具体的图上,通过拓扑排序,最短路等基础算法获得最小状态,从而解决问题。

在本题中,以 0a11 为节点,对于检点 v,遍历数组 a,将 v(v+aj)moda1 连一条权值为 aj 的边。把图建完之后,就形成了一个有向图,跑最短路后 disv 的值即为用题目给出的数能获得的最小的,对 a1 取模为 v 的值。

v, 1 到 n 中有 ndisva1+1 数是可以用题目给出的数到达,所以答案为 ia11rdisva1l1disva1

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << '=' << x << endl

inline int rd()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

void print(int x)
{
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
    return;
}

namespace Star_F
{
    #define int long long
    const int maxn = 13, N = 5e5 + 10, M = 11 * 5e5 + 10;
    int n, l, r;
    int a[maxn];
    struct edge
    {
        int u, v, w, nxt;
    } e[M];
    int tot, head[N];
    void add(int u, int v, int w) { e[++tot] = {u, v, w, head[u]}, head[u] = tot; }
    struct node
    {
        int u, dis;
        bool operator<(const node &a) const { return dis > a.dis; }
    };
    int vis[N], dis[N];
    void init() { 
        FOR(i, 1, N - 1)
        dis[i] = 1e18 + 10,
        vis[i] = 0; 
    }
    void dj(int s)
    {
        init();
        dis[s] = 0;
        priority_queue<node> q;
        q.push({s, dis[s]});
        while (!q.empty())
        {
            int u = q.top().u, k = q.top().dis;
            q.pop();
            if (vis[u] && k != dis[u])
                continue;
            vis[u] = 1;
            for (int i = head[u]; i; i = e[i].nxt)
            {
                int v = e[i].v, w = e[i].w;
                if (dis[v] > dis[u] + w)
                {
                    dis[v] = dis[u] + w;
                    q.push({v, dis[v]});
                }
            }
        }
    }
    int minn = 1e18 + 10, id = 0;
    int sov(int x)
    {
        int ans = 0;
        FOR(i, 0, minn - 1)
        {
            if (dis[i] <= x)
            {
                ans += (x - dis[i]) / minn + 1;
            }
        }
        return ans;
    }
    void Main()
    {
        cin >> n >> l >> r;
        FOR(i, 1, n)
        {
            cin >> a[i];
            if (a[i] == 0)
            {
                i--;
                n--;
                continue;
            }
            if (minn > a[i])
                minn = a[i], id = i;
        }
        FOR(i, 0, minn - 1)
        {
            FOR(j, 1, n)
            {
                if (a[j] == minn)
                    continue;
                add(i, (i + a[j]) % minn, a[j]);
            }
        }
        dj(0);
        int ans = sov(r) - sov(l - 1);
        cout << ans;
    }
}
signed main(){
    //freopen("inq.in","r",stdin);
    //freopen("inq.out","w",stdout);
    return Star_F::Main(), 0;
    return 0;
}

P2446 [SDOI2010] 大陆争霸

由题可知,有的城市被保护。设 uv 保护,我们从 v 建一条有向边到 u ,并记录 u 的入度 inu 。广度遍历图,在摧毁 v 时,删去 vu 的边,并更新入度。当 inu=0 时,方可进入城市 u

arrivei 表示到达 i 的时间(可能要在门口等待)。设 intoi 为进入 i 的时间(即什么时候所有保护 i 的城市被摧毁了)。设 disi表示摧毁 i 的时间,可得 disi=max(arrivei,intoi) 。设 {jn}i 的所有前驱,则 intoi=max{intojn}

#include <bits/stdc++.h>
using namespace std;
const int N=3001,M=200001;
struct edge{
    int u,nxt,v;
}e[M];
int he[N],n,m,tote;
struct Graph{
    int u,nxt;
}G[M];
int hg[N],totg;
struct node{
    int key,len;
    friend bool operator < (node x,node y){
        return x.len<y.len;
    }
};
int dis[N],into[N],in[N],arr[N];
bool vis[N];
priority_queue<node> q;
void dijkstra(int s){
    for(int i=1;i<=n;i++)
        dis[i]=arr[i]=1e9;
    dis[s]=into[s]=arr[s]=0;
    in[s]=0;
    q.push({s,0});
    int u,v;
    while(!q.empty()){
        u=q.top().key;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int k=he[u];k;k=e[k].nxt){
            v=e[k].u;
            if(dis[u]+e[k].v<arr[v]){
                arr[v]=dis[u]+e[k].v;
                if(!in[v]){
                    dis[v]=max(into[v],arr[v]);
                    q.push({v,-dis[v]});
                }
            }
        }
        for(int k=hg[u];k;k=G[k].nxt){
            v=G[k].u;
            into[v]=max(into[v],dis[u]);
            in[v]--;
            if(!in[v]){
                dis[v]=max(into[v],arr[v]);
                q.push({v,-dis[v]});
            }
        }
    }
}
int main(){
    cin >> n >> m;
    while(m--){
        int x,y,l;
        cin >> x >> y >> l;
        e[++tote].u = y, e[tote].nxt = he[x], e[tote].v = l, he[x] = tote;
    }
    for(int i=1;i<=n;i++){
        int x, y;
        cin >> x;
        while(x--){
            cin >> y;
            ++in[i], G[++totg].u = i, G[totg].nxt = hg[y], hg[y] = totg;
        }
    }
    dijkstra(1);
    cout << dis[n] << endl;
    return 0;
}

P8817 [CSP-S 2022] 假期计划

  • uv 之间可达意为 uv 之间可以不多于 k 次转车到达,及 uv 的距离不多于 k+1

  • 一个点 u 在家附近,意为 u 和 1 之间可达。

注意,为方便,特殊地,我们认为自己和自己不可达。

观察数据范围,n2 可过,已经足够处理出每个点对之间的距离了。因此首先应该采用 BFS,n2 计算任意点对之间是否可达。

注意:对于满足边权全为 1 的图,单源最短路可以做到 O(n) 的复杂度(采用 BFS);

对于满足边权只有 0,1 两种的图,单源最短路也可以做到 O(n) 的复杂度(采用 0-1 BFS)。

我们考虑一条 1abcd1 的路径,n4 的枚举是不可行的。

很直接的想法是,依次考虑 abcd,发现贪心是不对的,从 1 选择了更大的权值景点 a,但是可能接下来能到达的 b 就小的可怜;而选一个权值稍小一点的 a,可能可达的 b 会很大。

但有一种贪心是对的,那就是确定了 abc,选择 d 的时候。如果我们确定了 c,那么直接选择 c 可达的,在家附近的,不为 a 也不为 b 的权值最大的景点作为 d 即可。反正 d 之后没有景点了,所以可以直接贪心,没有后顾之忧。

由于环的对称性,可以发现确定了 bcd 之后,也可以贪心地选择 a

有了大体思路。

我们定义 f(u) 表示 u 可达,且在家附近的权值最大的景点。

第一步:我们预处理出 f(u)

第二步:直接 n2 枚举,循环确定景点 bcbc),然后记 a=f(b)d=f(c),然后试图用 w(a)+w(b)+w(c)+w(d) 更新答案,其中 w(u) 表示景点 u 的权值。

发现重复性的细节是存在问题的,比如:会出现 f(b)=c 的情况,此时让 a=f(b) 会导致 a=c,这是不允许的。

不过,贪心思想还是不变的:a 应该尝试更换为 u 可达,且在家附近的权值第二大的景点。

更换定义,f(u,k) 表示 u 可达,且在家附近的权值第 k 大的景点。

当发现 f(b,1)=c 时,将 a 设置为 f(b,2) 即可。

当发现 f(c,1)=b 时,将 d 设置为 f(c,2) 即可。

发现并没有完全解决:如果这样处理后的 ad 仍然重复怎么办?

还是贪心思想,考虑把 a 或者 d 换成更小的那个比较一下就行,具体来说,比如原先 a=f(b,2),就把 a 下调为 f(b,3);或者原先 d=f(c,2),就把 d 下调为 f(c,3),然后比较两种方案哪种更好就行了。

重复性解决了,但是还有个细节:如果原先 a=f(b,1),发现 a=d,我们想下调 a。是直接把 a 设置成 f(b,2) 吗?错误,我们还需要检查一下 f(b,2) 是否等于 c,如果等于 c 还需要继续下调到 f(b,3)。下调 d 同理。

至于原先 a=f(b,2) 为啥下调 a 不需检查?因为如果原先 a=f(b,2) 了说明 f(b,1) 已经等于 c 了。所以 f(b,3) 肯定什么问题都没有。

如果直接这么写是没有问题的,但是麻烦了。下面是一种更好写的处理方法:

直接分别枚举 a 分别作为 f(b,1)f(b,2)f(b,3)d 分别作为 f(c,1)f(c,2)f(c,3) 组成的共九种情况,检查互异性然后更新答案即可。

需要提前预处理出 f(u,k3),其实只要在最开始 BFS 预处理的同时维护一下就行了。

注意某些点可达,还在家附近的点数可能没有 3 个,因此注意类似访问 f(b,3) 时产生的越界问题。

如果枚举到某个 bc,发现 b 或者 c 有对应点数不超过 3 的情况时,这组 bc 不一定可以生成一组合法的解,属于正常现象。

题目保证全局上是有解的。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 2510, M = 20010;

int n, m, k;
LL w[N];
int h[N], e[M], ne[M], idx;
int dist[N], f[N][4], q[N];
bool st[N][N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void bfs(int start, int f[])
{
    int hh = 0, tt = 0;
    memset(dist, 0x3f, sizeof dist);
    dist[start] = 0;
    q[0] = start;

    while (hh <= tt)
    {
        int t = q[hh ++ ];
        if (dist[t] == k + 1) continue;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + 1)
            {
                dist[j] = dist[t] + 1;
                st[start][j] = true;
                q[ ++ tt] = j;

                if (st[1][j])
                {
                    f[3] = j;
                    for (int u = 3; u; u -- )  
                        if (w[f[u]] > w[f[u - 1]])
                            swap(f[u], f[u - 1]);
                        else break;
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 2; i <= n; i ++ ) scanf("%lld", &w[i]);

    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }

    for (int i = 1; i <= n; i ++ )
        bfs(i, f[i]);

    LL res = 0;
    for (int b = 2; b <= n; b ++ )
        for (int c = 2; c <= n; c ++ )
            if (st[b][c])
                for (int x = 0; x < 3; x ++ )
                    for (int y = 0; y < 3; y ++ )
                    {
                        int a = f[b][x], d = f[c][y];
                        if (a && d && a != d && a != c && b != d)
                            res = max(res, w[a] + w[b] + w[c] + w[d]);
                    }

    printf("%lld\n", res);
    return 0;
}

__EOF__

  • 本文作者: Star_F
  • 本文链接: https://www.cnblogs.com/fanrunze/p/18705261
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • posted @   Star_F  阅读(3)  评论(0编辑  收藏  举报
    相关博文:
    阅读排行:
    · 本地部署 DeepSeek:小白也能轻松搞定!
    · 如何给本地部署的DeepSeek投喂数据,让他更懂你
    · 从 Windows Forms 到微服务的经验教训
    · 李飞飞的50美金比肩DeepSeek把CEO忽悠瘸了,倒霉的却是程序员
    · 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee
    点击右上角即可分享
    微信分享提示