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
", ans);
} else {
printf("%d
", 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])高度为h[j]的全1矩阵, 至于这个矩阵的贡献前期预处理,直接o(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
", ans);
}