2020 計蒜之道 線上決賽
阿新 • • 發佈:2020-10-26
2020 計蒜之道 線上決賽
C. 攀登山峰
題解: 這題我比賽的時候想著如果用莫隊解決這題, 想了 很久都是 \(o(n ^ {1.5} * log(n))\) 的演算法, 就是優化不了log
後來看了題解才知道直接用主席樹, 模板主席不是可以記錄區間的數的總個數嘛, 就所有總共數大於(r - l + 1) / t
的都找一般最後在取一個最大值就好了。
那麼問題來這樣複雜度可以過嗎?
可以的因為t也就是20所以最壞的情況也就是將20個區間都找了一邊每次詢問的複雜度就是 log(n) * t
程式碼:
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 7; struct hjt{ int sum, l, r; } tree[40 * N]; int n, q, a[N], rt[N], top = 1; #define m (l + r) / 2 void update(int pos, int l, int r, int last, int &now) { now = top++; tree[now] = tree[last]; tree[now].sum++; if (l == r) return; if (pos <= m) update(pos, l, m, tree[last].l, tree[now].l); else update(pos, m + 1, r, tree[last].r, tree[now].r); } int query(int k, int l, int r, int last, int now) { if (l == r) { return l; } int sum = tree[tree[now].l].sum - tree[tree[last].l].sum; int sum1 = tree[tree[now].r].sum - tree[tree[last].r].sum; int ans = -1; if (sum1 >= k) { ans = max(ans, query(k, m + 1, r, tree[last].r, tree[now].r)); } if (sum >= k) { ans = max(ans, query(k, l, m, tree[last].l, tree[now].l)); } return ans; } vector<int> g; int get_id(int x) { return lower_bound(g.begin(), g.end(), x) - g.begin() + 1; } int main() { scanf("%d %d", &n, &q); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); g.push_back(a[i]); } sort(g.begin(), g.end()); g.erase(unique(g.begin(), g.end())); for (int i = 1; i <= n; i++) { a[i] = get_id(a[i]); update(a[i], 1, n, rt[i - 1], rt[i]); } while (q--) { int l, r, t; scanf("%d %d %d", &l, &r, &t); int k = (r - l + 1) / t; k++; int ans = query(k, 1, n, rt[l - 1], rt[r]); if (ans == -1) { printf("%d\n", ans); } else { printf("%d\n", g[ans - 1]); } } }
E. 矩陣
題解:這題很容易想到單調棧取維護。列舉(i,j) , 然後計算 以(i, j)這點為矩陣且(i, j)在底邊的貢獻。
這樣子計算就解決了去重的問題。
具體話, 等於每一層, 用一個\(lmin[j]\)表示右邊第一個小於 \(h[j]\)的元素(h[j]表示當前這層 j的最大高度)
那麼 \([j, lmin[j] - 1]\)就可以構造一個高度為 \(h[j]\) 的矩陣, 左邊也同理, 用\(lmin[j]\)表示左邊第一個小於或等於
h[j]的元素, 注意這個加粗, 這樣做的目的是為去重。仔細想想你就明白了
然後等於 j這個點就可以構造出一個 \([rmin[j] - 1, lmin[j] - 1]\)
程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e3 + 7; int mp[N][N]; ll h[N], cnt[N][N] ,lmin[N], rmin[N]; ll sum[N][N]; stack<int> q; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { scanf("%1d", &mp[i][j]); } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i % j == 0 || j % i == 0) { cnt[i][j]++; } } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { cnt[i][j] += cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1]; } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { sum[i][j] += sum[i - 1][j] + 1ll * cnt[i][j]; } } ll ans = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (mp[i][j] == 0) { h[j] = 0; } else { h[j]++; } } for (int j = 1; j <= n; j++) { while (!q.empty() && h[j] < h[q.top()]) { lmin[q.top()] = j; q.pop(); } q.push(j); } while (!q.empty()) { lmin[q.top()] = n + 1; q.pop(); } for (int j = n; j; j--) { while (!q.empty() && h[j] <= h[q.top()]) { rmin[q.top()] = j; q.pop(); } q.push(j); } while (!q.empty()) { rmin[q.top()] = 0; q.pop(); } for (int j = 1; j <= n; j++) { int len = lmin[j] - rmin[j] - 1; int l1 = j - rmin[j]; int l2 = lmin[j] - j; ll cat = sum[l1 + l2 - 1][h[j]] - sum[l1 - 1][h[j]] - sum[l2 - 1][h[j]]; ans += cat; } } printf("%lld\n", ans); }