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直接秒了,而且是图论,不是很懂,以后再补。。
- scu图论专题题解
- SCU
- SCU
- SCU
- SCU
- 【SCU
- SCU
- SCU
- SCU
- (SCU
- SCU
- SCU
- SCU
- SCU
- DP专题练习题解
- 【题解】UESTC_DP专题
- [kuangbin带你飞]专题九 连通图 题解报告
- 【图论专题】8.10~8.12 你见过大蒟蒻写题解吗?点进来你就见过了!
- python 装饰器
- 二叉树中最远的两个结点的距离
- RecyclerView关于图片复用
- php excel类 phpExcel使用方法介绍
- c: main()中int argc, char* argv[]参数的用法
- scu图论专题题解
- 最大公约数与最小公倍数
- 安检那些事情
- C语言注释转化为C++注释
- 欢迎使用CSDN-markdown编辑器
- [平衡树] 黑匣子 (blackbox)
- Two Sum
- 代理模式
- 特效相机妙用