总结
有点活过来了
- 并查集可以用来维护连通块,还可以用来维护自身的特殊信息。
- 做题要大胆猜结论。
- 数据随机的题目考虑乱搞。
- 对于一类 (a_i-a_jle i - j) 的式子,可以移项搞一下转化为 (a_i-ile{a_j-j}) 从而消除某些限制。
- 负无穷和 (0) 不是一种东西。
今日已完成
-
某场模拟赛 网格图
/* 知识点:并查集 学到了什么: 并查集可以用来维护连通块,还可以用来维护自身的特殊信息。 */ const int dx[] = {0, 0, -1, 1}; const int dy[] = {1, -1, 0, 0}; char s[A][A]; int n, m, fa[A * A], ans; inline int id(int x, int y) { return x * (m + 1) + y; } int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } namespace Right { //表示每个点右边的第一个0 //用并查集可以快速维护 int ff[A * A]; int Find(int x) { return x == ff[x] ? x : ff[x] = Find(ff[x]); } void Union(int x, int y) { int fx = Find(x), fy = Find(y); ff[fx] = fy; } } int main() { n = read(), m = read(); for (int i = 1; i <= n; i++) scanf("%s", s[i] + 1); int all = (n + 1) * (m + 1); for (int i = 1; i <= all; i++) fa[i] = Right::ff[i] = i; //首先计算出连通块的个数 //再之后更新 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s[i][j] != '1') continue; //先标记为一个新的连通块 ans++; for (int k = 0; k < 4; k++) { int bx = i + dx[k], by = j + dy[k]; if (s[bx][by] != '1') continue; int fz1 = find(id(i, j)), fz2 = find(id(bx, by)); if (fz1 != fz2) { //两者相邻但不为同一连通块 //要标记成同一连通块,所以ans-- ans--; if (rand() & 1) swap(fz1, fz2); fa[fz1] = fz2; } } Right::Union(id(i, j), id(i, j + 1)); } } cout << ans << ' '; int Q = read(); while (Q--) { int X1 = read(), Y1 = read(), X2 = read(), Y2 = read(); for (int x = X1; x <= X2; x++) { //找x,y1右边的第一个0,看是否在y2左边 //如果在左边就像上面一样更新,否则直接退出。 int pos = Right::Find(id(x, Y1)); int goal = id(x, Y2); while (pos <= goal) { int y = pos % (m + 1); if (!y) y = m + 1; ans++; for (int k = 0; k < 4; k++) { int bx = x + dx[k], by = y + dy[k]; if (s[bx][by] != '1') continue; int fz1 = find(pos), fz2 = find(id(bx, by)); if (fz1 != fz2) { ans--; if (rand() & 1) swap(fz1, fz2); fa[fz1] = fz2; } } s[x][y] = '1'; Right::Union(pos, id(x, y + 1)); pos = Right::Find(id(x, y)); } } cout << ans << ' '; } }
-
某场模拟赛 && 洛谷 P1439 最长公共子序列
这考试竟然考板子,真是爱了爱了。
int n, a[A], b[A], cx[A], f[A], len = 0; int main() { n = read(); for (int i = 1; i <= n; i++) a[i] = read(), cx[a[i]] = i; for (int i = 1; i <= n; i++) b[i] = read(); f[++len] = cx[b[1]]; for (int i = 2; i <= n; i++) { if (cx[b[i]] >= f[len]) f[++len] = cx[b[i]]; else { int k = lower_bound(f + 1, f + 1 + len, cx[b[i]]) - f; f[k] = cx[b[i]]; } } cout << len << ' '; return 0; }
-
A. 【18提高7】模仿游戏
数据随机,爆搜+剪枝可过
void dfs(int cnt) { if (cnt == m) { for (int i = 0; i < m; i++) cout << a[i] << " "; puts(""); for (int i = 0; i < m; i++) cout << b[i] << " "; puts(""); exit(0); } for (int i = 0; i < m; i++) { if (vis[i]) continue; a[cnt] = i; tot = 0; int flag = 0, siz = ys[cnt].size(); for (int j = 0; j < siz; j++) { int xj = ys[cnt][j], goal = (a[cnt] + (xj - 1) / m) % m; if (b[goal] != y[xj] && b[goal] != -1) { flag = 1; break; } if (b[goal] == -1) c[++tot] = goal, b[goal] = y[xj]; } if (flag) { for (int j = 1; j <= tot; j++) b[c[j]] = -1; continue; } vis[i] = 1; dfs(cnt + 1); vis[i] = 0; for (int j = 1; j <= tot; j++) b[c[j]] = -1; } } int main() { n = read(), m = read(); for (int i = 1; i <= n; i++) x[i] = read(); for (int i = 1; i <= n; i++) y[i] = read(); memset(b, -1, sizeof(b)); for (int i = 1; i <= n; i++) { int goal = (x[i] + i - 1) % m; ys[goal].push_back(i); } dfs(0); return 0; } /* 0 1 6 10 3 4 5 11 9 8 13 2 7 12 7 3 0 4 5 11 1 13 10 2 9 6 12 8 */
-
洛谷 P4290 [HAOI2008]玩具取名
区间DP基础题
char s[A]; int ok[5][5][5], f[A][A][5], le[A]; int calc(char s) { if (s == 'W') return 1; else if (s == 'I') return 2; else if (s == 'N') return 3; else if (s == 'G') return 4; } int main() { for (int i = 1; i <= 4; i++) le[i] = read(); for (int i = 1; i <= 4; i++) for (int j = 1; j <= le[i]; j++) { scanf("%s", s); ok[i][calc(s[0])][calc(s[1])] = 1; } scanf("%s", s + 1); int n = strlen(s + 1); for (int i = 1; i <= n; i++) f[i][i][calc(s[i])] = 1; for (int len = 1; len <= n; len++) { for (int l = 1; l <= n - len + 1; l++) { int r = (l + len); for (int mi = l; mi < r; mi++) for (int zt1 = 1; zt1 <= 4; zt1++) if (f[l][mi][zt1]) for (int zt2 = 1; zt2 <= 4; zt2++) if (f[mi + 1][r][zt2]) for (int zt3 = 1; zt3 <= 4; zt3++) if (ok[zt3][zt1][zt2]) f[l][r][zt3] = 1; } } int flag = 0; if (f[1][n][1]) flag = 1, cout << "W"; if (f[1][n][2]) flag = 1, cout << "I"; if (f[1][n][3]) flag = 1, cout << "N"; if (f[1][n][4]) flag = 1, cout << "G"; if (!flag) puts("The name is wrong!"); return 0; }
-
CF1433F Zero Remainder Sum
非常好玩的状态设计
int n, m, k, a[A][A], f[A][A][A], g[A][A]; //设f_{j,cnt,r} 表示当前行选了 cnt 个数的和 //且 cnt 个数的和 mod k = r int main() { n = read(), m = read(), k = read(); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) a[i][j] = read(); memset(g, 0xcf, sizeof(g)); g[0][0] = 0; for (int i = 1; i <= n; i++) { memset(f, 0xcf, sizeof(f)); //debug:把初始化放在了j里 for (int j = 0; j <= m; j++) f[j][0][0] = 0; for (int j = 1; j <= m; j++) { for (int cnt = 1; cnt <= m / 2; cnt++) for (int r = 0; r < k; r++) f[j][cnt][r] = max(f[j - 1][cnt][r], f[j - 1][cnt - 1][((r - a[i][j]) % k + k) % k] + a[i][j]); } for (int j = 0; j < k; j++) { for (int cnt = 0; cnt <= m / 2; cnt++) for (int r = 0; r < k; r++) g[i][j] = max(g[i][j], g[i - 1][((j - f[m][cnt][r]) % k + k) % k] + f[m][cnt][r]); } } cout << max(0, g[n][0]) << ' '; return 0; }