scu图论专题题解

来源:互联网 发布:淘宝达人广场 在哪里 编辑:程序博客网 时间:2024/06/05 14:35

传送门:点击打开链接

感觉做着还是好费劲啊,,,有些东西总是想不到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|1struct 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 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 交通事故对方全责但不赔偿怎么办 人格分列症的症状怎么办 子宫肌瘤月经量多怎么办 卵泡长得太慢怎么办 后期卵泡长得慢怎么办 促排卵泡长得慢怎么办 子宫内膜薄要怎么办啊 hcg值正常孕酮低怎么办 20号染色体三体怎么办 14号染色体三体怎么办 怀孕七个月不想要了怎么办 胎儿性染色体45x怎么办 16号染色体偏多怎么办 大拇指又短又宽怎么办 削山药皮皮肤痒怎么办 脊柱侧弯20度怎么办 27岁脊柱侧弯怎么办 右侧侧脑室增宽怎么办 左侧脑室增宽该怎么办 腿上的血管堵塞怎么办 做b超看不清骶尾怎么办 孕中期羊水过少怎么办 心脏办膜关闭不全怎么办 9个月胎儿脑积水怎么办 怀孕三个月胎盘低置怎么办 怀孕第一个月打针了怎么办 唐氏筛查神经管缺陷高风险怎么办 门诊处方笺丢了怎么办 孕中期睡觉手麻怎么办 怀孕2个月了没胎心胎芽怎么办 怀孕腿疼的厉害怎么办 孕妇老是失眠多梦怎么办 孕妇会失眠多梦怎么办 怀孕5个月睡不着怎么办 6个月孕妇失眠怎么办 彩超脉络丛囊肿怎么办 双侧脉络丛囊肿怎么办 唐筛神经管缺陷高风险怎么办 雌激素低怎么办吃什么东西补 我怀了狗的孩子怎么办 结婚2年不要孩子怎么办