2019牛客多校第八场
A. All-one Matices
solved at 01:58(+2)
求一个\(n*m\)的\(01\)矩阵的极大全\(1\)子矩阵数目
悬线法处理出\(d\)数组(从这个位置最多向上延伸多少个\(1\)),然后单调栈处理出每个位置的\(d\)能延伸的左右最远位置,\(vis\)打标记的时候如果发现标记不是\(i-1\)也不是\(0\)说明这里之前有一个极大矩阵
#include <bits/stdc++.h> using namespace std; const int N = 3010; char a[N][N]; int d[N][N], s[N][N], s2[N][N], n, m, vis[N][N], ans; int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) { scanf("%s", a[i] + 1); } for(int j = 1; j <= m; ++j) { for(int i = 1; i <= n; ++i) { if(a[i][j] == '0') d[i][j] = 0; else d[i][j] = d[i - 1][j] + 1; } } for(int i = 1; i <= n; ++i) { stack<int> st; while(!st.empty()) st.pop(); d[i][m + 1] = -1; st.push(m + 1); for(int j = m; j; --j) { while(d[i][st.top()] >= d[i][j]) st.pop(); s[i][j] = st.top() - 1; st.push(j); } } for(int i = 1; i <= n; ++i) { stack<int> st; while(!st.empty()) st.pop(); d[i][0] = -1; st.push(0); for(int j = 1; j <= m; ++j) { while(d[i][st.top()] >= d[i][j]) st.pop(); s2[i][j] = st.top() + 1; st.push(j); } } for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { if(a[i][j] == '0') continue; int r = s[i][j], l = s2[i][j]; if(vis[l][r] == i) continue; if(vis[l][r] != i - 1 && vis[l][r]) ans++; vis[l][r] = i; } } for(int i = 1; i <= m; ++i) for(int j = i; j <= m; ++j) ans += vis[i][j] != 0; printf("%d\n", ans); return 0; }
B. Beauty Values
solved at 00:13
定义beauty values为数组中不同的数的数量,求所有区间的beauty values 之和
期望的线性性
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int n, a[N]; long long ans; vector<int> pos[N]; int main() { scanf("%d", &n); for(int i = 1; i < N; ++i) pos[i].push_back(0); for(int i = 1; i <= n; ++i) { scanf("%d", &a[i]); pos[a[i]].push_back(i); } for(int i = 1; i < N; ++i) { for(int j = 1; j < pos[i].size(); ++j) { ans += 1LL * (pos[i][j] - pos[i][j - 1]) * (n - pos[i][j] + 1); } } printf("%lld\n", ans); return 0; }
C. CDMA
solved at 01:30
构造一个\(m*m\)的矩阵,元素均为正负一,要求满足任意两行的相应位置乘积之和为\(0\),\(m\)为2的幂次
首先手推出\(m=2\)的解(其实样例已经给了),然后把它复制三边得到一个\(4*4\)的矩阵,把右下角的\(2*2\)个元素取反,就得到了\(m=4\)的解,以此类推即可(我是用的\(m=4\)作为初始矩阵往下推得)
#include <bits/stdc++.h> using namespace std; int d[4][4] = { {1, 1, 1, 1}, {1, 1, -1, -1}, {1, -1, 1, -1}, {1, -1, -1, 1} }; int m; int f(int i, int j, int mod) { if(mod == 4) return 1; mod /= 2; return (i >= mod && j >= mod ? -1 : 1) * f(i % mod, j % mod, mod); } int main() { scanf("%d", &m); if(m == 2) { puts("1 1\n1 -1"); return 0; } for(int i = 0; i < m; ++i) { for(int j = 0; j < m; ++j) { printf("%d\t", f(i, j, m) * d[i % 4][j % 4]); } puts(""); } return 0; }
D. Distance
upsolved
有一个三维空间,有两种操作,给一个点打上标记,或者查询给定点到所有已标记点中的最小曼哈顿距离,保证三维空间长宽高乘积不超过\(1e5\)
可以定期重构\(bfs\),但是显然三维树状数组更短
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int n, m, h, q, op, x, y, z; struct BIT { static int lowbit(int x) {return x & -x;} static void mmax(int &x, int y) {x = (x > y ? x : y);} static int index(int i, int j, int k) {return (i - 1) * m * h + (j - 1) * h + k;} int c[N]; void init() {memset(c, 0xc0, sizeof(c));} void update(int x, int y, int z, int val) { for(int i = x; i <= n; i += lowbit(i)) for(int j = y; j <= m; j += lowbit(j)) for(int k = z; k <= h; k += lowbit(k)) mmax(c[index(i, j, k)], val); } int query(int x, int y, int z) { int res = -1e9; for(int i = x; i; i -= lowbit(i)) for(int j = y; j; j -= lowbit(j)) for(int k = z; k; k -= lowbit(k)) res = max(res, c[index(i, j, k)]); return res; } }T[8]; int main() { scanf("%d%d%d%d", &n, &m, &h, &q); for(int i = 0; i < 8; ++i) T[i].init(); while(q--) { scanf("%d%d%d%d", &op, &x, &y, &z); if(op == 1) { for(int mask = 0; mask < 8; ++mask) { int tx = x, ty = y, tz = z, vx = x, vy = y, vz = z; if(mask & 1) tx = n - tx + 1, vx = -vx; if(mask & 2) ty = m - ty + 1, vy = -vy; if(mask & 4) tz = h - tz + 1, vz = -vz; T[mask].update(tx, ty, tz, vx + vy + vz); } } else { int res = 1e9; for(int mask = 0; mask < 8; ++mask) { int tx = x, ty = y, tz = z, vx = x, vy = y, vz = z; if(mask & 1) tx = n - tx + 1, vx = -vx; if(mask & 2) ty = m - ty + 1, vy = -vy; if(mask & 4) tz = h - tz + 1, vz = -vz; res = min(res, vx + vy + vz - T[mask].query(tx, ty, tz)); } printf("%d\n", res); } } return 0; }
E. Explorer
upsolved
无向图,每条边有上下界限制,只有在上下界之中的数字可以通过这条边,询问有多少种数字可以从\(1\)走到\(n\)
时间分治线段树+可撤销并查集
把size限制看成时间限制就好了
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int u[N], v[N], l[N], r[N], b[N << 1], n, m, tot, fa[N], sz[N]; vector<int> g[N << 3]; stack<pair<int*, int>> stk[22]; long long ans; int find(int x) { return x== fa[x] ? x : find(fa[x]); } void update(int rt, int l, int r, int L, int R, int id) { if(L <= l && r <= R) {g[rt].push_back(id); return;} int mid = l + r >> 1; if(L <= mid) update(rt << 1, l, mid, L, R, id); if(R > mid) update(rt << 1 | 1, mid + 1, r, L, R, id); } void dfs(int rt, int l, int r, int dep) { while(!stk[dep].empty()) stk[dep].pop(); for(auto i: g[rt]) { int x = find(u[i]), y = find(v[i]); if(x == y) continue; if(sz[x] < sz[y]) swap(x, y); stk[dep].push(make_pair(&sz[x], sz[x])); stk[dep].push(make_pair(&fa[y], fa[y])); sz[x] += sz[y]; fa[y] = x; } if(l == r) { if(find(1) == find(n)) ans += b[r + 1] - b[l]; } else { int mid = l + r >> 1; dfs(rt << 1, l, mid, dep + 1); dfs(rt << 1 | 1, mid + 1, r, dep + 1); } while(!stk[dep].empty()) { *(stk[dep].top().first) = stk[dep].top().second; stk[dep].pop(); } } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i) { scanf("%d%d%d%d", &u[i], &v[i], &l[i], &r[i]); b[2 * i - 1] = l[i]; b[2 * i] = ++r[i]; } sort(b + 1, b + 2 * m + 1); tot = unique(b + 1, b + 2 * m + 1) - b - 1; for(int i = 1; i <= m; ++i) { l[i] = lower_bound(b + 1, b + tot + 1, l[i]) - b; r[i] = lower_bound(b + 1, b + tot + 1, r[i]) - b - 1; update(1, 1, tot, l[i], r[i], i); } for(int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1; dfs(1, 1, tot, 0); printf("%lld\n", ans); return 0; }
G. Gemstones
solved at 00:17
签到题,用栈贪心即可