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直接秒了,而且是图论,不是很懂,以后再补。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值