莫队总结
省赛打得跟屎一样=_=,队友有思路了然鹅我们都不会写莫队,调不出来。回来恶补一波莫队。
不带修改莫队
通过对离线询问的一个玄学排序,使得左右指针移动的次数都相对的少,减少转移的次数
排序:左端点不同块的询问,按左端点所属块号升序排; 左端点同块的询问,按右端点升序排
奇偶性优化:对于左端点同块的询问,若左端点块号为奇数,按右端点升序排;为偶数则按右端点降序排。 可以减少右端点移动的次数。
题意:查询区间[l, r]中差小于等于k的
a
i
a_i
ai和
a
j
a_j
aj的对数
解法:用权值树状数组维护莫队当前
[
l
,
r
]
[l, r]
[l,r]中的各数的数量,莫队增加一个数x时,x贡献的答案为query(x - k, x + k),删除时同理
放一个回来写的省赛c代码,因为不能补,不能保证没bug。
(5.19更新)
今天在hdu开重现了,wa了几发。树状数组开小了,因为最多可能一共有3n个点。乘个3能过。
#include <bits/stdc++.h>
#define query(x, y) (sum(y) - sum(x-1))
using namespace std;
int n, m, k, a[30000], len, ans[30000];
vector<int> b;
struct node {
int l, r, id;
bool operator<(const node &a) const {
if (l / len == a.l / len)
return (l / len) & 1 ? r < a.r : r > a.r;
return l / len < a.l / len;
}
} q[30000];
struct {
int l, m, r;
} id[30000];
int c[30000];
void add(int p, int val) {
for (int i = p; i <= n; i += i & -i) {
c[i] += val;
}
}
int sum(int p) {
int ans = 0;
for (int i = p; i; i -= i & -i) {
ans += c[i];
}
return ans;
}
int main() {
scanf("%d%d%d", &n, &m, &k);
len = sqrt(n);
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
b.push_back(a[i]);
b.push_back(a[i] + k);
b.push_back(a[i] - k);
}
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end());
for (int i = 1; i <= n; ++i) {
id[i].m = lower_bound(b.begin(), b.end(), a[i]) - b.begin() + 1;
id[i].l = lower_bound(b.begin(), b.end(), a[i] - k) - b.begin() + 1;
id[i].r = lower_bound(b.begin(), b.end(), a[i] + k) - b.begin() + 1;
}
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &q[i].l, &q[i].r);
q[i].id = i;
}
sort(q + 1, q + m + 1);
int l = 1, r = 0, now = 0;
n = b.size();
for (int i = 1; i <= m; ++i) {
while (l > q[i].l) {
--l;
now += query(id[l].l, id[l].r);
add(id[l].m, 1);
}
while (r < q[i].r) {
++r;
now += query(id[r].l, id[r].r);
add(id[r].m, 1);
}
while (l < q[i].l) {
add(id[l].m, -1);
now -= query(id[l].l, id[l].r);
++l;
}
while (r > q[i].r) {
add(id[r].m, -1);
now -= query(id[r].l, id[r].r);
--r;
}
ans[q[i].id] = now;
}
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
return 0;
}
带修改莫队
有修改的时候块大小要开成
n
2
/
3
n^{2/3}
n2/3才能使时间复杂度达到最优(
O
(
n
5
/
3
)
O(n^{5/3})
O(n5/3))
在前面的基础上加上一维时间,并把询问和修改分别记录。
对于两个询问,若左端点块号和右端点都相同,则按时间升序排序
在莫队的四个while处理好区间后,再用两个while把当前时间调整到询问的时间。
对于一个修改,在time增加时处理到它时应把a[i]改为val, 那么在time减少又遇到它时,显然应该把当前的a[i]改回原来的a[i]
所以处理修改时直接swap(a[i], val),把原来的值存到修改里面,详情见代码
BZOJ2120数颜色
#include <bits/stdc++.h>
#define query(x, y) (sum(y) - sum(x-1))
using namespace std;
const int maxn = 50005;
int n, m, a[maxn], len, ans[maxn];
struct node {
int l, r, t, id;
node() { t = 0; }
bool operator<(const node &a) const {
if (l / len != a.l / len)
return l / len < a.l / len;
if (r != a.r)
return (l / len) & 1 ? r < a.r : r > a.r;
return t < a.t;
}
} q[maxn];
struct {
int pos, val;//修改的位置和值
} c[maxn];
int cnt[1000005];
int main() {
scanf("%d%d", &n, &m);
len = (int) pow(n, 2.0 / 3) + 1;
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
}
char op[5];
int l, r, qnum = 0, cnum = 0;
for (int i = 1; i <= m; ++i) {
scanf("%s", op);
if (op[0] == 'Q') {
++qnum;
scanf("%d%d", &q[qnum].l, &q[qnum].r);
q[qnum].id = qnum;
q[qnum].t = cnum;
}
else {
++cnum;
scanf("%d%d", &c[cnum].pos, &c[cnum].val);
}
}
sort(q + 1, q + qnum + 1);
l = 1, r = 0;
int now = 0, time = 0;
for (int i = 1; i <= m; ++i) {
while (l > q[i].l) {
if (++cnt[a[--l]] == 1)
++now;
}
while (r < q[i].r) {
if (++cnt[a[++r]] == 1)
++now;
}
while (l < q[i].l) {
if (--cnt[a[l++]] == 0)
--now;
}
while (r > q[i].r) {
if (--cnt[a[r--]] == 0)
--now;
}
while (time < q[i].t) {
++time;
if (l <= c[time].pos && c[time].pos <= r) {
if (--cnt[a[c[time].pos]] == 0)
--now;
if (++cnt[c[time].val] == 1)
++now;
}
swap(c[time].val, a[c[time].pos]);
}
while (time > q[i].t) {
if (l <= c[time].pos && c[time].pos <= r) {
if (--cnt[a[c[time].pos]] == 0)
--now;
if (++cnt[c[time].val] == 1)
++now;
}
swap(c[time].val, a[c[time].pos]);
--time;
}
ans[q[i].id] = now;
}
for (int i = 1; i <= qnum; ++i) {
printf("%d\n", ans[i]);
}
return 0;
}
树上莫队
对于子树询问,dfs序把树拍平乱搞就行了
链上的询问还没学,好像是欧拉序?这个之后补
放一个子树询问的代码。
CodeForces - 375D Tree and Queries
询问子树中数量大于等于k的颜色的数量
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
vector<int> G[maxn];
int color[maxn], cnt, L[maxn], R[maxn], ans[maxn], c[maxn], a[maxn], ANS[maxn];
void dfs(int u, int fa) {
L[u] = ++cnt;
c[cnt] = color[u];
for (int &v:G[u]) {
if (v == fa) continue;
dfs(v, u);
}
R[u] = cnt;
}
int len;
struct node {
int l, r, k, id;
bool operator<(const node &b) const {
if (l / len != b.l / len)
return l / len < b.l / len;
return l / len & 1 ? r < b.r : r > b.r;
}
} q[maxn];
int num[maxn];
int main() {
int n, m, u, v, k;
cin >> n >> m;
len = sqrt(n);
for (int i = 1; i <= n; ++i) {
cin >> color[i];
}
for (int i = 0; i < n - 1; ++i) {
cin >> u >> v;
G[u].emplace_back(v);
G[v].emplace_back(u);
}
dfs(1, 1);
for (int i = 1; i <= m; ++i) {
cin >> v >> q[i].k;
q[i].l = L[v];
q[i].r = R[v];
q[i].id = i;
}
sort(q + 1, q + m + 1);
int l = 1, r = 0;
for (int i = 1; i <= m; ++i) {
while (l > q[i].l) {
ANS[++num[c[--l]]]++;
}
while (r < q[i].r) {
ANS[++num[c[++r]]]++;
}
while (l < q[i].l) {
ANS[num[c[l++]]--]--;
}
while (r > q[i].r) {
ANS[num[c[r--]]--]--;
}
ans[q[i].id] = ANS[q[i].k];
}
for (int i = 1; i <= m; ++i) {
cout << ans[i] << '\n';
}
return 0;
}
也可以树上启发式合并
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int n, m, color[maxn], ans[maxn];
vector<int> G[maxn];
int siz[maxn], son[maxn];
void dfs1(int u, int fa)
{
siz[u] = 1;
for (int &v:G[u])
{
if (v == fa) continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > siz[son[u]])
son[u] = v;
}
}
struct query
{
int k, id;
};
vector<query> q[maxn];
int cntColor[maxn], num[maxn];
bool vis[maxn];
void solve(int u, int fa, int val)
{
if (val == -1)
--num[cntColor[color[u]]--];
else
++num[++cntColor[color[u]]];
for (int &v:G[u])
if (v != fa && !vis[v])
solve(v, u, val);
}
void dfs2(int u, int fa, bool flag)
{
for (int &v:G[u])
{
if (v == fa || v == son[u]) continue;
dfs2(v, u, true);
}
if (son[u])
{
dfs2(son[u], u, false);
vis[son[u]] = true;
}
solve(u, fa, 1);
for (auto i:q[u])
ans[i.id] = num[i.k];
if (son[u])
vis[son[u]] = false;
if (flag)
solve(u, fa, -1);//消去轻儿子的贡献
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> color[i];
int u, v;
for (int i = 1; i < n; ++i)
{
cin >> u >> v;
G[u].emplace_back(v);
G[v].emplace_back(u);
}
int k;
for (int i = 1; i <= m; ++i)
{
cin >> u >> k;
q[u].push_back({k, i});
}
dfs1(1,1);
dfs2(1,1, true);
for (int i = 1; i <= m; ++i)
cout << ans[i] << '\n';
return 0;
}