暴力枚举,略。
简单题,就是题意可能不是很好懂…
听说是个不是很难的矩阵快速幂,略。
高斯消元还原多项式,略。
经典推箱子问题,略。除了代码长长长之外没啥特别值得提的地方。
#include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int dx[][2] = {{-1, -1}, {2, 2}, {0, 1}, {0, 1}}; const int dy[][2] = {{0, 1}, {0, 1}, {-1, -1}, {2, 2}}; const int ddx[] = {-1, 1, 0, 0}, ddy[] = {0, 0, -1, 1}; int n, m, dis[55][55][4], dis2[2][55][55]; char s[55][55]; queue<pair<int, int> > q; struct Node{ int x, y, dir, d; Node(int _x, int _y, int _dir, int _d): x(_x), y(_y), dir(_dir), d(_d){} bool operator < (const Node& nd) const{ return d > nd.d; } }; priority_queue<Node> pq; void bfs(int stx, int sty, int o){ q.emplace(stx, sty); memset(dis2[o], 0x3f, sizeof(dis2[o])); dis2[o][stx][sty] = 0; while (!q.empty()){ int x = q.front().first, y = q.front().second; q.pop(); int dd = dis2[o][x][y]; for (int i = 0, cx, cy; i < 4; ++i){ cx = x + ddx[i], cy = y + ddy[i]; if (s[cx][cy] == '*') continue; if (dis2[o][cx][cy] == INF) dis2[o][cx][cy] = dd + 1, q.emplace(cx, cy); } } } void dijkstra(){ while (!pq.empty()){ int x = pq.top().x, y = pq.top().y, dir = pq.top().dir; int dd = pq.top().d; pq.pop(); if (dd > dis[x][y][dir]) continue; // go one step int cx = x + ddx[dir], cy = y + ddy[dir]; if (dis[cx][cy][dir ^ 1] > dd + 1) dis[cx][cy][dir ^ 1] = dd + 1, pq.push(Node(cx, cy, dir ^ 1, dd + 1)); // to another direction s[x][y] = s[x][y + 1] = s[x + 1][y] = s[x + 1][y + 1] = '*'; bfs(x + dx[dir][0], y + dy[dir][0], 0); bfs(x + dx[dir][1], y + dy[dir][1], 1); s[x][y] = s[x][y + 1] = s[x + 1][y] = s[x + 1][y + 1] = '.'; for (int i = 0, cx1, cy1, cx0, cy0; i < 4; ++i){ cx0 = x + dx[i][0], cy0 = y + dy[i][0]; cx1 = x + dx[i][1], cy1 = y + dy[i][1]; if (s[cx0][cy0] != '*' && s[cx1][cy1] != '*'){ int res = INF; res = min(res, dis2[0][cx0][cy0] + dis2[1][cx1][cy1]); res = min(res, dis2[1][cx0][cy0] + dis2[0][cx1][cy1]); if (res < INF && dis[x][y][i] > dd + res) dis[x][y][i] = dd + res, pq.push(Node(x, y, i, dd + res)); } } } } int main(){ for (; ; ){ scanf("%d%d", &n, &m); if (!n && !m) break; for (int i = 1; i <= n; ++i) scanf("%s", s[i] + 1); for (int i = 0; i <= m + 1; ++i) s[0][i] = s[n + 1][i] = '*'; for (int i = 0; i <= n + 1; ++i) s[i][0] = s[i][m + 1] = '*'; vector<pair<int, int> > vec; int bx = -1, by; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j){ if (s[i][j] == 'X' && bx == -1) bx = i, by = j; else if (s[i][j] == '.') vec.push_back(make_pair(i, j)); } if (bx == 1 && by == 1){ printf("0\n"); continue ; } memset(dis, 0x3f, sizeof(dis)); s[bx][by] = s[bx][by + 1] = s[bx + 1][by] = s[bx + 1][by + 1] = '*'; bfs(vec[0].first, vec[0].second, 0); bfs(vec[1].first, vec[1].second, 1); s[bx][by] = s[bx][by + 1] = s[bx + 1][by] = s[bx + 1][by + 1] = '.'; while (!pq.empty()) pq.pop(); for (int i = 0, cx1, cy1, cx0, cy0; i < 4; ++i){ cx0 = bx + dx[i][0], cy0 = by + dy[i][0]; cx1 = bx + dx[i][1], cy1 = by + dy[i][1]; if (s[cx0][cy0] != '*' && s[cx1][cy1] != '*'){ int res = INF; res = min(res, dis2[0][cx0][cy0] + dis2[1][cx1][cy1]); res = min(res, dis2[1][cx0][cy0] + dis2[0][cx1][cy1]); if (res < INF) dis[bx][by][i] = res, pq.push(Node(bx, by, i, res)); } } dijkstra(); int ans = min(dis[1][1][1], dis[1][1][3]); if (ans == INF) printf("-1\n"); else printf("%d\n", ans); } return 0; }带权并查集,略。
听说是一个不很难的圆求交,略。
题意:有 n n n 个集合, m m m 个限制。限制有 5 种(第 i i i 个集合用 X i X_i Xi 表示):
X i ⊂ X j X_i \subset X_j Xi⊂Xj X i = X j X_i = X_j Xi=Xj X i ≠ X j X_i \neq X_j Xi=Xj X i ∩ X j = ∅ X_i \cap X_j = \varnothing Xi∩Xj=∅ X i ∩ X j ≠ ∅ X_i \cap X_j \neq \varnothing Xi∩Xj=∅问最多能满足前多少个限制。
先二分,然后每次只要考虑前若干个限制是否能同时满足即可。
考虑维护“子集”以及“相交为空”这两种关系,以及记录每一个集合是否为空。先处理前两种限制,求出包含关系的传递闭包,然后再处理第四种限制,将 X i X_i Xi 和 X j X_j Xj 的“下游集合”(即同时是两者的子集的集合)标记为空集。
最后检查第三种限制和第五种限制,前者很容易判定,后者需要先判定 X i X_i Xi 或者 X j X_j Xj 是空集,然后找出是否存在 X k X_k Xk 和 X l X_l Xl,使得 X k ∩ X l = ∅ , X i ⊂ X k , X j ⊂ X l X_k \cap X_l = \varnothing, X_i \subset X_k, X_j \subset X_l Xk∩Xl=∅,Xi⊂Xk,Xj⊂Xl。这部分我不太会,只写了一个暴力,居然还比较快。
时间复杂度理论上是 O ( m n 2 log m ) O(mn^2 \log m) O(mn2logm),但可以通过。
#include <bits/stdc++.h> #define REP(temp, init_val, end_val) for (int temp = init_val; temp <= end_val; ++temp) #define REPR(temp, init_val, end_val) for (int temp = init_val; temp >= end_val; --temp) using namespace std; int read(){ int f = 1, x = 0; char c = getchar(); while (c < '0' || c > '9'){if(c == '-') f = -f; c = getchar();} while (c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar(); return f * x; } int n, m, cons[10005][3]; bool f[105][105], g[105][105], isempty[105]; bool check(int x){ memset(f, 0, sizeof(f)); memset(g, 0, sizeof(g)); memset(isempty, 0, sizeof(isempty)); REP(i, 1, n) f[i][i] = true; REP(i, 1, x){ if (cons[i][0] <= 2) f[cons[i][1]][cons[i][2]] = true; if (cons[i][0] == 2) f[cons[i][2]][cons[i][1]] = true; } REP(k, 1, n) REP(i, 1, n) REP(j, 1, n) f[i][j] = f[i][j] || (f[i][k] && f[k][j]); REP(i, 1, x){ if (cons[i][0] == 4){ int u = cons[i][1], v = cons[i][2]; g[u][v] = g[v][u] = true; // 如果在这里预处理所有 g 的传递关系就会 TLE。 REP(j, 1, n) if (f[j][u] && f[j][v]) isempty[j] = true; } } REP(i, 1, x){ if (cons[i][0] == 3){ int u = cons[i][1], v = cons[i][2]; if (f[u][v] && f[v][u]) return false; if (isempty[u] && isempty[v]) return false; } else if (cons[i][0] == 5){ int u = cons[i][1], v = cons[i][2]; if (isempty[u] || isempty[v]) return false; REP(k, 1, n){ if (!f[u][k]) continue; REP(l, 1, n){ if (f[v][l] && g[k][l]) return false; } } } } return true; } void solve(){ int l = 1, r = m; while (l < r){ int mid = (l + r + 1) >> 1; if (check(mid)) l = mid; else r = mid - 1; } printf("%d\n", l); } int main(){ for (; ; ){ n = read(), m = read(); if (!n && !m) break; REP(i, 1, m){ REP(j, 0, 2) cons[i][j] = read(); } solve(); } return 0; }简单二分,略。注意“最后一行可以不向右对齐”这个限制。
待补。。。
比较神奇的一场。
H 这种找到最小限制的题目还是比较考验水平的。
I 这种卡上下界的写法还是要多多注意。