传送门:点击打开链接
感觉做着还是好费劲啊,,,有些东西总是想不到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直接秒了,而且是图论,不是很懂,以后再补。。