scu圖論專題題解

傳送門:點擊打開鏈接

感覺做着還是好費勁啊,,,有些東西總是想不到T^T


A題:給出邊,分別判斷是有向圖或者無向圖的時候,是否爲歐拉回路

首先必須弱連通圖要連通。對於無向圖,度全爲偶數,或者度爲奇數的點的個數爲2

對於有向圖,入度全部等於出度,或者1個點入度-出度=1,1個點出度-入度=1,其他點入度等於出度

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 1000 + 5;
int P[MX], IN[MX], OUT[MX];
int find(int x) {
    return P[x] == x ? x : (P[x] = find(P[x]));
}
int main() {
    int T, n, m; //FIN;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        memset(IN, 0, sizeof(IN));
        memset(OUT, 0, sizeof(OUT));
        for(int i = 1; i <= n; i++) P[i] = i;
        for(int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            IN[v]++; OUT[u]++;
            u = find(u); v = find(v);
            P[v] = u;
        }
        bool a1 = 0, a2 = 0;
        int c1 = 0, c2 = 0, c3 = 0;
        for(int i = 1; i <= n; i++) {
            if((IN[i] + OUT[i]) % 2) c1++;
        }
        if(c1 == 2 || c1 == 0) a1 = 1;

        c1 = 0;
        for(int i = 1; i <= n; i++) {
            if(IN[i] - OUT[i] == 1) c1++;
            if(IN[i] - OUT[i] == -1) c2++;
            if(IN[i] == OUT[i]) c3++;
        }
        if(c3 == n || (c3 == n - 2 && c1 == 1 && c2 == 1)) a2 = 1;

        c1 = 0;
        for(int i = 1; i <= n; i++) {
            if(i == find(i)) c1++;
        }
        if(c1 != 1) a1 = a2 = 0;
        printf("%s %s\n", a1 ? "Yes" : "No", a2 ? "Yes" : "No");
    }
    return 0;
}



B題:

我猜標程是想考,如果覆蓋,就建一條邊,那麼最後只要看是否能做拓撲排序,就有答案了。

不過這題也有暴力方法,因爲數據比較小,每次我都刪掉最上面的那個窗口,直到全部刪掉即可

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 10 + 5;
int s[MX][MX];
inline bool ok(int i, int j, int w) {
    if(s[i][j] == 0 || s[i][j] == w) return 1;
    return 0;
}
bool solve() {
    while(1) {
        int tot = 0, flag = 0;
        for(int i = 1; i <= 3; i++) {
            for(int j = 1; j <= 3; j++) {
                tot++;
                if(ok(i, j, tot) && ok(i + 1, j, tot) && ok(i, j + 1, tot) && ok(i + 1, j + 1, tot)) {
                    int sum = s[i][j] + s[i + 1][j] + s[i][j + 1] + s[i + 1][j + 1];
                    if(sum) s[i][j] = s[i + 1][j] = s[i][j + 1] = s[i + 1][j + 1] = 0, flag = 1;
                }
            }
        }
        if(!flag) break;
    }
    int w = 0;
    for(int i = 1; i <= 4; i++) {
        for(int j = 1; j <= 4; j++) {
            w += s[i][j];
        }
    }
    return w == 0;
}
int main() {
    int T;//FIN;
    scanf("%d", &T);
    while(T--) {
        for(int i = 1; i <= 4; i++) {
            for(int j = 1; j <= 4; j++) {
                scanf("%d", &s[i][j]);
            }
        }
        printf("%s\n", solve() ? "Lucky dog!" : "BOOM!");
    }
    return 0;
}



C題:對於任意兩個字符,如果ASCII碼之差絕對值<=1,則就會連一條邊。只有a,b,c三種字符

現在告訴你一個圖,問是否有這樣的滿足題意的字符集合。

當一個點連着所有其他的點的話,我們就讓這個點的字符爲b,這樣一定是最優的

那麼對於a和c,很明顯是一個二分圖,我們接下來就類似判定圖是否是二分圖的方法,01染色即可

不過要注意,如果i到j有一條邊,j點不是字符b,那麼j的字符必須和i的字符一樣

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 500 + 5;
int n, m, w[MX];
bool G[MX][MX], vis[MX];
bool DFS(int u, int x) {
    vis[u] = 1; w[u] = x;
    for(int v = 1; v <= n; v++) {
        if(u == v) continue;
        if(G[u][v]) {
            if(vis[v]) {
                if(w[v] == (x ^ 1)) return 0;
            } else if(!DFS(v, x)) return 0;
        } else {
            if(vis[v]) {
                if(w[v] != (x ^ 1)) return 0;
            } else if(!DFS(v, x ^ 1)) return 0;
        }
    }
    return 1;
}
bool solve() {
    for(int i = 1; i <= n; i++) {
        if(!vis[i] && !DFS(i, 0)) return 0;
    }
    return 1;
}
int main() {
    //FIN;
    int T; scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        memset(G, 0, sizeof(G));
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u][v] = G[v][u] = 1;
        }
        for(int i = 1; i <= n; i++) {
            int cnt = 0;
            for(int j = 1; j <= n; j++) {
                cnt += G[i][j];
            }
            if(cnt == n - 1) {
                vis[i] = 1; w[i] = 2;
            }
        }
        printf("%s\n", solve() ? "Yes" : "No");
    }
    return 0;
}




D:告訴所有人的座標,和2個食堂的座標。有的人互相喜歡,必須要去同一個食堂,有的人互相討厭,不能在一個食堂。要求任意兩個人所走的距離+兩個人選擇的食堂直接的距離之和最小。

一個人有2種狀態,要麼1食堂,要麼2食堂,我們會比較容易想到2sat。那麼假如我們直接二分答案,對於前面的討厭和喜歡的,我們很容易就能連出邊。

之後我們2個for循環,枚舉任意一對人,然後再枚舉他們分別要去哪個食堂,看這種情況是否符合要求,如果不符合,就連邊讓這種情況禁止

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 6000 + 5;
const int INF = 0x3f3f3f3f;
struct Edge {
    int v, nxt;
} E[5000005];
int Head[MX][2], erear;
void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int z, int u, int v) {
    E[erear].v = v;
    E[erear].nxt = Head[u][z];
    Head[u][z] = erear++;
}
void edge_add(int u, int v) {
    edge_add(0, u, v);
    edge_add(1, v, u);
}
int Stack[MX], Belong[MX], vis[MX], ssz, bsz;
void DFS(int u, int s) {
    vis[u] = 1;
    if(s) Belong[u] = s;
    for(int i = Head[u][s > 0]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(!vis[v]) DFS(v, s);
    }
    if(!s) Stack[++ssz] = u;
}
void tarjan(int n) {
    ssz = bsz = 0;
    for(int i = 1; i <= n; i++) vis[i] = 0;
    for(int i = 1; i <= n; i++) {
        if(!vis[i]) DFS(i, 0);
    }
    for(int i = 1; i <= n; i++) vis[i] = 0;
    for(int i = ssz; i >= 1; i--) {
        if(!vis[Stack[i]]) DFS(Stack[i], ++bsz);
    }
}
int n, A, B;
struct Point {
    int x, y;
} P[MX], din[2];
int lku[MX], lkv[MX];
int ulku[MX], ulkv[MX];
int dist(Point a, Point b) {
    return abs(a.x - b.x) + abs(a.y - b.y);
}
bool check(int m) {
    edge_init();
    for(int i = 1; i <= A; i++) {
        edge_add(ulku[i], ulkv[i] + n); edge_add(ulku[i] + n, ulkv[i]);
        edge_add(ulkv[i], ulku[i] + n); edge_add(ulkv[i] + n, ulku[i]);
    }
    for(int i = 1; i <= B; i++) {
        edge_add(lku[i], lkv[i]); edge_add(lkv[i], lku[i]);
        edge_add(lku[i] + n, lkv[i] + n); edge_add(lkv[i] + n, lku[i] + n);
    }
    for(int i = 1; i <= n; i++) {
        for(int j = i + 1; j <= n; j++) {
            for(int a = 0; a <= 1; a++) {
                for(int b = 0; b <= 1; b++) {
                    if(dist(P[i], din[a]) + dist(P[j], din[b]) + dist(din[a], din[b]) > m) {
                        edge_add(!a ? i : i + n, !b ? j + n : j);
                        edge_add(!b ? j : j + n, !a ? i + n : i);
                    }
                }
            }
        }
    }
    tarjan(2 * n);
    for(int i = 1; i <= n; i++) {
        if(Belong[i] == Belong[i + n]) return false;
    }
    return true;
}
int solve() {
    int l = 0, r = 1e7, m;
    if(!check(r)) return -1;
    while(l <= r) {
        m = (l + r) >> 1;
        if(check(m)) r = m - 1;
        else l = m + 1;
    }
    return r + 1;
}
int main() {
    int T;//FIN;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d", &n, &A, &B);
        scanf("%d%d%d%d", &din[0].x, &din[0].y, &din[1].x, &din[1].y);
        for(int i = 1; i <= n; i++) {
            scanf("%d%d", &P[i].x, &P[i].y);
        }
        for(int i = 1; i <= A; i++) {
            scanf("%d%d", &ulku[i], &ulkv[i]);
        }
        for(int i = 1; i <= B; i++) {
            scanf("%d%d", &lku[i], &lkv[i]);
        }
        printf("%d\n", solve());
    }
    return 0;
}



E題:敢寫就敢過啊。。

先跑強連通分量,之後我們能很明顯的發現,如果在一個組的話,那麼這個組裏面的拓撲序就是唯一的。我們很容易就想到了二分圖的最小路徑覆蓋。

不過複雜度如果在極限情況下,應該是蠻差的,如果不敢用匈牙利算法,那就寫更快的,雖然我不會寫

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 1e4 + 5;
const int INF = 0x3f3f3f3f;
struct Edge {
    int u, v, nxt;
} E[200005];
int Head[MX], erear;

void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v) {
    E[erear].u = u;
    E[erear].v = v;
    E[erear].nxt = Head[u];
    Head[u] = erear++;
}


int n, m, IN[MX], cnt[MX], val[MX];
int bsz, ssz, dsz;
int Low[MX], DFN[MX];
int belong[MX], Stack[MX];
bool inStack[MX];
void Init_tarjan(int n) {
    bsz = ssz = dsz = 0;
    for(int i = 1; i <= n; ++i) Low[i] = DFN[i] = 0;
}
void Tarjan(int u) {
    Stack[++ssz] = u;
    inStack[u] = 1;
    Low[u] = DFN[u] = ++dsz;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(!DFN[v]) {
            Tarjan(v);
            Low[u] = min( Low[v], Low[u]);
        } else if(inStack[v]) {
            Low[u] = min( Low[u], DFN[v]);
        }
    }
    if(Low[u] == DFN[u]) {
        ++bsz;
        int v;
        do {
            v = Stack[ssz--];
            inStack[v] = 0;
            belong[v] = bsz;
        } while(u != v);
    }
}
int match[MX];
bool vis[MX];
bool DFS(int u) {
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(!vis[v]) {
            vis[v] = 1;
            if(match[v] == -1 || DFS(match[v])) {
                match[v] = u;
                return 1;
            }
        }
    }
    return 0;
}
int BM(int n) {
    int res = 0;
    memset(match, -1, sizeof(match));
    for(int u = 1; u <= n; u++) {
        memset(vis, 0, sizeof(vis));
        if(DFS(u)) res++;
    }
    return res;
}
int solve(int n) {
    Init_tarjan(n);
    for (int i = 1; i <= n; i++) {
        if (!DFN[i]) Tarjan(i);
    }
    edge_init();
    for(int i = 0; i < m; i++) {
        int u = E[i].u, v = E[i].v;
        u = belong[u]; v = belong[v];
        if(u != v) edge_add(u, v + bsz);
    }
    return bsz - BM(bsz);
}
int main() {
    int T; //FIN;
    scanf("%d", &T);
    while(T--) {
        edge_init();
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            edge_add(u, v);
        }
        printf("%d\n", solve(n));
    }
    return 0;
}



F題:令x爲一個環中的邊的最大值,那麼有多少個環,我們就得到了多少個x。答案要求這些x中的最小的那個

這題非常有意思啊,通常看到環,我們可能會想到強連通分量,不過這個要求所有的環,強連通是做不到的。

我們先求出一個最小生成樹。然後我們再枚舉所有的邊。

如果這條邊是生成樹上的,我們就忽略這條邊,因爲這條邊不能組成環

如果這條邊不是生成樹上的,那麼我們把u和v連起來,就會和樹上的路徑形成環。又因爲是最小生成樹,這條邊的權值一定會大於路徑上任意一條邊的權值,所以對於這個環,邊長的最大值就等於我們枚舉到的這條邊的權值。

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;

const int MX = 5e5 + 5;
const int INF = 0x3f3f3f3f;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

struct Edge {
    int u, v, cost;
    bool operator<(const Edge &P) const {
        return cost < P.cost;
    }
} A[2000005];

bool ok[MX];
int n, m, P[MX];
int find(int x) {
    return P[x] == x ? x : (P[x] = find(P[x]));
}
void MST_solve() {
    sort(A + 1, A + 1 + m);
    for(int i = 1; i <= n; i++) P[i] = i;
    for(int i = 1; i <= m; i++) {
        int p1 = find(A[i].u), p2 = find(A[i].v);
        if(p1 != p2) {
            P[p1] = p2;
            ok[i] = 1;
        }
    }
}
int main() {
    int T;//FIN;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        memset(ok, 0, sizeof(ok));
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d", &A[i].u, &A[i].v, &A[i].cost);
        }
        MST_solve();
        int ans = INF;
        for(int i = 1; i <= m; i++) {
            if(!ok[i]) ans = min(ans, A[i].cost);
        }
        if(ans == INF) printf("No solution!\n");
        else printf("%d\n", ans);
    }
    return 0;
}


G題:我們先求出哪些邊會構成最短路。只要從起點出發求一次,從終點出發求一次,然後枚舉邊,看從起點到u的最短距離+邊長+終點到v的最短距離=起點到終點的最短距離,那麼這條邊就是能構成最短路的。我們得到後來這個DAG模型後,假設起點有INF條流,然後分配給下面的節點,類似網絡流的思想,只是沒有容量的概念,所以胡亂向下更新就行了。

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;

const int MX = 1e3 + 5;
const int MS = 2e5 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;

struct Edge {
    bool ok, vis;
    int u, v, w, nxt;
} E[MS];
int Head[MX], erear;
void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v, int w) {
    E[erear].u = u;
    E[erear].v = v;
    E[erear].w = w;
    E[erear].ok = E[erear].vis = 0;
    E[erear].nxt = Head[u];
    Head[u] = erear++;
}
int n, m, op, ed;
int val[MX];
LL d[2][MX], path;
typedef pair<LL, int> PLI;
void dijkstra(int u, LL d[], int o) {
    for(int i = 1; i <= n; i++) d[i] = INF;
    priority_queue<PLI, vector<PLI>, greater<PLI> > Q;
    d[u] = 0; Q.push(PLI(d[u], u));
    while(!Q.empty()) {
        PLI ftp = Q.top(); Q.pop();
        LL dist = ftp.first; int u = ftp.second;
        if(dist > d[u]) continue;
        for(int i = Head[u]; ~i; i = E[i].nxt) {
            if((i & 1) != o) continue;
            int v = E[i].v, w = E[i].w;
            if(d[u] + w < d[v]) {
                d[v] = d[u] + w;
                Q.push(PLI(d[v], v));
            }
        }
    }
}
int solve() {
    queue<int> Q;
    memset(val, 0, sizeof(val));
    Q.push(op); val[op] = INF;
    while(!Q.empty()) {
        int u = Q.front(); Q.pop();
        for(int i = Head[u]; ~i; i = E[i].nxt) {
            if(!E[i].ok || E[i].vis || !val[u]) continue;
            E[i].vis = 1; val[u]--;
            Q.push(E[i].v); val[E[i].v]++;
        }
    }
    return val[ed];
}
int main() {
    //FIN;
    int T; scanf("%d", &T);
    while(T--) {
        edge_init();

        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            edge_add(u, v, w);
            edge_add(v, u, w);
        }
        scanf("%d%d", &op, &ed);
        dijkstra(op, d[0], 0);
        path = d[0][ed];
        dijkstra(ed, d[1], 1);

        for(int i = 0; i < erear; i += 2) {
            int u = E[i].u, v = E[i].v, w = E[i].w;
            if(d[0][u] + w + d[1][v] == path) {
                E[i].ok = 1;
            }
        }
        printf("%d\n", solve());
    }
    return 0;
}



H題:被題目迷惑了。。

冷靜的想一下,就是要從起點到終點,所花費的時間<=k,並且經過的路徑裏的cap的最小值最大。

這種讓什麼什麼最大的題,先無腦二分想一想,發現好像非常有道理的樣子。。我們直接二分答案,之後跑logn次最短路就可以了

不過我第一次TLE了,再在最短路里加個特判,如果此時路徑距離已經>k了,就直接返回

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

typedef pair<LL, int>PLI;
const int MX = 1e5 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;

struct Edge {
    int v, nxt, w, cap;
} E[MX];
int Head[MX], erear;
void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v, int cap, int w) {
    E[erear].v = v;
    E[erear].w = w;
    E[erear].cap = cap;
    E[erear].nxt = Head[u];
    Head[u] = erear++;
}

int n, m, k;
LL d[MX];
LL dijkstra(int u, int m) {
    for(int i = 1; i <= n; i++) d[i] = INF;
    priority_queue<PLI, vector<PLI>, greater<PLI> >Q;
    d[u] = 0; Q.push(PLI(0, u));
    while(!Q.empty()) {
        PLI ftp = Q.top(); Q.pop();
        int u = ftp.second;
        LL dist = ftp.first;
        if(dist > d[u]) continue;
        if(dist > k) return d[n];
        for(int i = Head[u]; ~i; i = E[i].nxt) {
            int v = E[i].v, w = E[i].w, cap = E[i].cap;
            if(cap >= m && d[u] + w < d[v]) {
                d[v] = d[u] + w;
                Q.push(PLI(d[v], v));
            }
        }
    }
    return d[n];
}
bool check(int m) {
    return dijkstra(1, m) <= k;
}
int main() {
    int T;//FIN;
    scanf("%d", &T);
    while(T--) {
        edge_init();
        scanf("%d%d%d", &n, &m, &k);
        LL l = 2e9, r = 1, mid;
        for(int i = 1; i <= m; i++) {
            int u, v, cap, dis;
            scanf("%d%d%d%d", &u, &v, &cap, &dis);
            edge_add(u, v, cap, dis);
            edge_add(v, u, cap, dis);
            l = min(l, (LL)cap);
            r = max(r, (LL)cap);
        }
        if(!check(l)) {
            printf("Poor RunningPhoton!\n");
            continue;
        }
        while(l <= r) {
            mid = (l + r) >> 1;
            if(check(mid)) l = mid + 1;
            else r = mid - 1;
        }
        printf("%lld\n", l - 1);
    }
    return 0;
}



I題:tarjan求割邊

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;

const int MX = 2e5 + 5;
const int INF = 0x3f3f3f3f;

int rear;
int Head[MX], Next[MX];
int Low[MX], DFN[MX], dfs_clock;
int cut[MX];

struct Edge {
    int u, v, sign;
} E[MX];

void edge_init() {
    rear = 0;
    memset(Head, -1, sizeof(Head));
    memset(Next, -1, sizeof(Next));
}

void edge_add(int u, int v) {
    E[rear].u = u;
    E[rear].v = v;
    E[rear].sign = false;
    Next[rear] = Head[u];
    Head[u] = rear++;
}

void tarjan_init() {
    dfs_clock = 0;
    memset(DFN, 0, sizeof(DFN));
    memset(cut, 0, sizeof(cut));
}
int tarjan(int u, int e) {
    Low[u] = DFN[u] = ++dfs_clock;

    int child = 0;
    for(int id = Head[u]; ~id; id = Next[id]) {
        int v = E[id].v;

        if(!DFN[v]) {
            int lowv = tarjan(v, id | 1);
            Low[u] = min(Low[u], lowv);

            if(lowv >= DFN[u]) {
                cut[u] = 1;
            }
            if(lowv > DFN[u]) {
                E[id].sign = 1;
                E[id ^ 1].sign = 1;
            }

            child++;
        } else if((id | 1) != e && DFN[v] < DFN[u]) {
            Low[u] = min(Low[u], DFN[v]);
        }
    }

    if(e == -1 && child == 1) cut[u] = 0;
    return Low[u];
}

int main() {
    //FIN;
    int T, ansk = 0;
    scanf("%d", &T);
    while(T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        edge_init();
        tarjan_init();
        for(int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            edge_add(u, v);
            edge_add(v, u);
        }

        for(int i = 1; i <= n; i++) {
            if(!DFN[i]) tarjan(i, -1);
        }

        int ans = 0;
        for(int i = 0; i < rear; i += 2) {
            if(E[i].sign) ans++;
        }
        printf("Case %d: %d\n", ++ansk, ans);
    }
    return 0;
}




J題:首先我們求一遍強連通分量然後縮點,然後就變成了DAG模型。

然後我們能發現,這也是二分圖的最小路徑覆蓋中的經典題。不過這裏邊是可以重複經過的,我們需要多增加許多條邊。如果u能通過有向邊到v,那麼我們就在u連一條邊到v。這一部分我們直接用bitset然後記憶化搜索,然後再建邊,複雜度只有O(n^2),比floyd稍微快一些,而且還蠻好用的。

理論上這題的複雜度用BM也是通過不了的,不過敢寫敢過啊,過不了再換更快的求二分圖匹配的寫法啊,嘿嘿嘿

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;

const int MX = 2e3 + 5;
const int INF = 0x3f3f3f3f;
struct Edge {
    int u, v, nxt;
} E[200005];
int Head[MX], erear;
void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v) {
    E[erear].u = u;
    E[erear].v = v;
    E[erear].nxt = Head[u];
    Head[u] = erear++;
}
int bsz, ssz, dsz;
int Low[MX], DFN[MX];
int belong[MX], Stack[MX];
bool inStack[MX];
void Init_tarjan(int n) {
    bsz = ssz = dsz = 0;
    for(int i = 1; i <= n; ++i) Low[i] = DFN[i] = 0;
}
void Tarjan(int u) {
    Stack[++ssz] = u;
    inStack[u] = 1;
    Low[u] = DFN[u] = ++dsz;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(!DFN[v]) {
            Tarjan(v);
            Low[u] = min(Low[v], Low[u]);
        } else if(inStack[v]) {
            Low[u] = min(Low[u], DFN[v]);
        }
    }
    if(Low[u] == DFN[u]) {
        ++bsz;
        int v;
        do {
            v = Stack[ssz--];
            inStack[v] = 0;
            belong[v] = bsz;
        } while(u != v);
    }
}

int n, m;
int match[MX];
bool vis[MX];
typedef bitset<1000> bits;
bits G[MX];
bool DFS(int u) {
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(!vis[v]) {
            vis[v] = 1;
            if(match[v] == -1 || DFS(match[v])) {
                match[v] = u;
                return 1;
            }
        }
    }
    return 0;
}
int BM(int n) {
    int res = 0;
    memset(match, -1, sizeof(match));
    for(int u = 1; u <= n; u++) {
        memset(vis, 0, sizeof(vis));
        if(DFS(u)) res++;
    }
    return res;
}
bits DP(int u) {
    if(G[u].count()) return G[u];
    G[u][u - 1] = 1;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        G[u] |= DP(v);
    }
    return G[u];
}
int solve(int n) {
    Init_tarjan(n);
    for (int i = 1; i <= n; i++) {
        if (!DFN[i]) Tarjan(i);
    }
    edge_init();
    for(int i = 0; i < m; i++) {
        int u = E[i].u, v = E[i].v;
        u = belong[u]; v = belong[v];
        if(u != v) edge_add(u, v);
    }
    for(int i = 1; i <= bsz; i++) G[i].reset();
    for(int i = 1; i <= bsz; i++) DP(i);

    edge_init();
    for(int i = 1; i <= bsz; i++) {
        for(int j = 1; j <= bsz; j++) {
            if(i != j && G[i][j - 1]) edge_add(i, j + bsz);
        }
    }
    return bsz - BM(bsz);
}
int main() {
    int T, ansk = 0; //FIN;
    scanf("%d", &T);
    while(T--) {
        edge_init();
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            edge_add(u, v);
        }
        printf("Case %d: %d\n", ++ansk, solve(n));
    }
    return 0;
}



K題:應該就是求強連通分量縮點,然後在DAG模型上按拓撲序亂搞一下。

不是特別懂題目意思,,不寫了- -


L題:bnu校賽原題,題解點擊打開鏈接



M題:不會,感覺是數論,但是Claris直接秒了,而且是圖論,不是很懂,以後再補。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章