LOJ6284. 数列分块入门 8 题解
作者:互联网
涉及操作:
- 区间查询某一个数 \(c\) 出现的次数;
- 区间更新。
解题思路:
一开始的思路是除了整块维护以外,再对每一个区间用一个 multiset 维护每一个数出现的次数。这样更新和查询一次的时间复杂度都会降到 \(O(\sqrt n \log n)\),总的时间复杂度是 \(O(n \cdot \sqrt n \log n)\),但是超时了,只有30分。
30分代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, blo, a[maxn], bl[maxn], tag[505]; // tag[i] 表示第i个分块在被完全覆盖时对应的那个数
bool flag[505]; // flag[i] 表示第i个分块是否全被一个数覆盖
unordered_multiset<int> st[505];
void recover(int p) {
if (flag[p]) {
flag[p] = false;
st[p].clear();
for (int i = (p-1)*blo+1; i <= min(p*blo, n); i ++) {
a[i] = tag[p];
st[p].insert(a[i]);
}
}
}
void update(int l, int r, int c) {
recover(bl[l]);
for (int i = l; i <= min(bl[l]*blo, r); i ++) {
st[bl[l]].erase(st[bl[l]].find(a[i]));
a[i] = c;
st[bl[l]].insert(c);
}
if (bl[l] != bl[r]) {
recover(bl[r]);
for (int i = (bl[r]-1)*blo+1; i <= r; i ++) {
st[bl[r]].erase(st[bl[r]].find(a[i]));
a[i] = c;
st[bl[r]].insert(c);
}
}
for (int i = bl[l]+1; i < bl[r]; i ++) {
flag[i] = true;
tag[i] = c;
}
}
int query(int l, int r, int c) {
int ans = 0;
recover(bl[l]);
for (int i = l; i <= min(bl[l]*blo, r); i ++)
if (a[i] == c)
ans ++;
if (bl[l] != bl[r]) {
recover(bl[r]);
for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
if (a[i] == c)
ans ++;
}
for (int i = bl[l]+1; i < bl[r]; i ++) {
if (flag[i] && tag[i] == c)
ans += blo;
else if (!flag[i])
ans += st[i].count(c);
}
return ans;
}
int main() {
ios::sync_with_stdio(0);
cin >> n;
blo = sqrt(n);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
bl[i] = (i - 1) / blo + 1;
st[bl[i]].insert(a[i]);
}
for (int i = 0; i < n; i ++) {
int l, r, c;
cin >> l >> r >> c;
cout << query(l, r, c) << endl;
update(l, r, c);
}
return 0;
}
然后改成用 map 试一试 手动狗头
(其实写上面这句话的时候我没感觉 map 能过,然后我就想要狗头试一下 map,所以我估计是 multiset 常数比较大吧;另一方面我也觉得原作者的那个写法时间复杂度是有点高的 https://loj.ac/d/3445)
然后过了…… (可能 multiset 比较耗时吧)
100分程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, blo, a[maxn], bl[maxn], tag[505]; // tag[i] 表示第i个分块在被完全覆盖时对应的那个数
bool flag[505]; // flag[i] 表示第i个分块是否全被一个数覆盖
// unordered_multiset<int> st[505];
map<int, int> mp[505];
void recover(int p) {
if (flag[p]) {
flag[p] = false;
mp[p].clear();
for (int i = (p-1)*blo+1; i <= min(p*blo, n); i ++) {
a[i] = tag[p];
mp[p][a[i]] ++;
}
}
}
void update(int l, int r, int c) {
recover(bl[l]);
for (int i = l; i <= min(bl[l]*blo, r); i ++) {
mp[bl[l]][a[i]] --;
a[i] = c;
mp[bl[l]][c] ++;
}
if (bl[l] != bl[r]) {
recover(bl[r]);
for (int i = (bl[r]-1)*blo+1; i <= r; i ++) {
mp[bl[r]][a[i]] --;
a[i] = c;
mp[bl[r]][a[i]] ++;
}
}
for (int i = bl[l]+1; i < bl[r]; i ++) {
flag[i] = true;
tag[i] = c;
}
}
int query(int l, int r, int c) {
int ans = 0;
recover(bl[l]);
for (int i = l; i <= min(bl[l]*blo, r); i ++)
if (a[i] == c)
ans ++;
if (bl[l] != bl[r]) {
recover(bl[r]);
for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
if (a[i] == c)
ans ++;
}
for (int i = bl[l]+1; i < bl[r]; i ++) {
if (flag[i] && tag[i] == c)
ans += blo;
else if (!flag[i])
ans += mp[i][c];
}
return ans;
}
int main() {
ios::sync_with_stdio(0);
cin >> n;
blo = sqrt(n);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
bl[i] = (i - 1) / blo + 1;
mp[bl[i]][a[i]] ++;
}
for (int i = 0; i < n; i ++) {
int l, r, c;
cin >> l >> r >> c;
cout << query(l, r, c) << endl;
update(l, r, c);
}
return 0;
}
标签:LOJ6284,分块,int,题解,flag,bl,blo,maxn,505 来源: https://www.cnblogs.com/quanjun/p/15527327.html